Base64 形式を扱う必要がありますか?それならこのサイトが最適です!データをエンコードまたはデコードするために便利なオンラインツールをご利用ください。

PowerShell での Base64 エンコード:完全ガイド

あなたの手元には文字列、ファイル、証明書、あるいはトークンがあり、向こう側のワイヤーはそれを英字と数字の長い列として欲しがっています:印字可能で、メール、URL、設定ファイルに貼り付け可能で、輸送を壊すバイナリのバイトが 1 つもない。それが Base64 です。それは翻訳であり、圧縮でも鍵でもありません:入力の 3 バイトが出力の 4 文字になるので、あなたが送るテキストは出発点より約 33% 大きくなり、使うのは 64 文字のアルファベットに、末尾パディングとしての等号です。

このサイトのホームページでは、アルファベット、ビットの計算、各変形を詳しく扱っています。この記事は、エンコードの方向を PowerShell 側から扱います:あなたが呼ぶ 1 つの .NET メソッド、最初のスクリプトで誰もがつまずく抜け落ちたステップ、プロトコルによって異なる行折り返しの慣習、URL 安全なアルファベット、そして PowerShell でのエンコードが慎重さを報い、雑さを罰する、ほんの一握りの実際の作業です。

メソッドと、抜け落ちたステップ

PowerShell には独自の Base64 cmdlet は同梱されていません。その仕事を担うのはメソッドで、2003 年の .NET Framework 1.1 から .NET フレームワークの一部であり、PowerShell 自体が登場する 3 年前のことです:

$bytes = [System.Text.Encoding]::UTF8.GetBytes("Hello")
[System.Convert]::ToBase64String($bytes)
# SGVsbG8=

これが API の全部です:バイト配列が 1 つ入って、文字列が 1 つ出てくる。あらゆる OS のあらゆる PowerShell で、理由は .NET であるだけだからです。抜け落ちたステップは、例の 1 行目で、初心者が最初の 1 時間を失う場所です。このメソッドはあなたの文字列を受け取りません。受け取るのはバイトで、「私の文字列は何のバイトを意味するのか」という問いは、答えられるのはあなただけのエンコーディングの問題です。PowerShell から実際に呼び出せるオーバーロードの契約を、以下に示します:

渡すもの 得られるもの
byte[] 標準 Base64 の長い 1 行。長さが要求するところで = パディング付き
byte[] に InsertLineBreaks 同じデータを、行間に CRLF を挟んで 76 文字ごとに折り返したもの
byte[]、オフセット、カウント 配列の要求されたスライスだけをエンコードしたもの
"Hello" のような文字列 変換例外。PowerShell は単独では、文字列をバイト配列に変えられません
$null ArgumentNullException が、MethodInvocationException に包まれて返ってきます

表にないものに注意してください:「このテキストをエンコードして」と言うオーバーロードはないのです。PowerShell でテキストをエンコードするのは、常に 2 ステップのプロセスです。エンコーディングを決定し、バイトを生成し、はじめて Base64 メソッドが会話に加わります。スクリプトの中で、その 2 つの判断を目に見える形で分けておいてください。2 つ目は見えないのに、バグが住んでいるのは 1 つ目だからです。

テキストのエンコード:まずエンコーディングを決める

現代のインターネットを渡るものすべてにとっての安全な既定値は、UTF-8 です。Web API、JSON、JWT、ブラウザやサーバーが過去 10 年に書いたものはすべて、Base64 の下層に UTF-8 バイトを期待します。2 ステップのパターンこそ、あなたが身に着けたい習慣です:

$text = "Hello, PowerShell!"
$bytes = [System.Text.Encoding]::UTF8.GetBytes($text)
$encoded = [System.Convert]::ToBase64String($bytes)
# SGVsbG8sIFBvd2VyU2hlbGwh

別のエンコーディングに手が伸びるとき、たいていあなたはレガシーシステムに向き合っています。下表が実務的なガイドです:

エンコーディング 使う場面 間違ったものを選んだとき
UTF8 Web API、JSON、JWT、現代的なすべて。既定の選択 向こう側のデコーダーは、あなたのテキストの代わりに文字化けを見る
Unicode(UTF-16LE) 消費側が .NET 文字列をエンコードする Windows か .NET のコンポーネント、または -EncodedCommand のとき ペイロードは消費側の期待の 2 倍の長さになり、驚きに満ちる
ASCII HTTP Basic 認証情報などの、従来の 7 ビットプロトコル 値 127 を超えるものは、エンコーディングが起きる前に置き換えられてしまう
Latin1 UTF-8 より前の、レガシーなヨーロッパのシステム 文字ごとに 1 バイトで、Latin-1 以外のすべての文字が疑問符になる

双方向で効く、役立つデバッグのコツがあります:Base64 のパディングと長さが、何バイトがエンコードされたかを教えてくれ、デコードされたテキストの見え方が、それが 2 バイトの世界から来たのか 1 バイトの世界から来たのかを教えてくれます。サイズが疑わしく偶数で、ふつうの文字と空白っぽい文字が交互に並んでいるペイロードは、たいてい UTF-8 の仮装をした UTF-16、またはその逆です。

UTF-16 へのサプライズ

PowerShell は内部で文字列を UTF-16 として保存し、その事実は、ある特定で非常に一般的な場所で Base64 作業に漏れ出します:消費側そのものが .NET か Windows のコンポーネントであるための Base64 を書き、.NET 文字列はそういうものだからと Unicode でエンコードする場面です。それは一部の消費側に対しては正しい直感で、残りのすべてに対してはサイズを 2 倍にする間違いです。同じ 4 つの見える文字が、2 つのエンコーディング:

$same = "Café"
[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($same))
# Q2Fmw6k=  5 バイト
[System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($same))
# QwBhAGYA6QA=  8 バイト

同じテキスト、2 倍のサイズ、そしてその 2 つの文字列は互換ではありません:片方を期待してもう片方を消費した消費側は、大きく失敗することはありません。ゴミを読み込むだけです。これに咬まれないようにするルールは、エンコーディングをローカルな詳細ではなく、プロトコルの一部として扱うことです。受信システムがブラウザ、REST API、モダンなサーバーなら、ドキュメントがそれ以外と言わない限り UTF-8 です。-EncodedCommand 経由の PowerShell ホスト自身、Windows のみのパイプライン内の .NET 文字列なら、UTF-16LE です。プロトコルが言及していないときは、向こう側に GetString を何で呼ぶのかを聞いてください。実際に決まるのは、あの問いだからです。

数値、バイト、そしてそのほかのもの

このメソッドはバイト配列を受け取ると宣言されていますが、PowerShell の型変換は、何がそれに含まれるかについては寛大です。エッジを知っておくと、驚きから身を守れます:

[System.Convert]::ToBase64String([byte[]](1, 2, 3, 250, 251))
# AQID+vs=
[System.Convert]::ToBase64String([char[]]"Café")
# Q2Fm6Q==  文字ごとに 1 バイト、その文字の値を数値として
[System.Convert]::ToBase64String([int[]](72, 101, 108, 108, 111))
# SGVsbG8=
[System.Convert]::ToBase64String(123)
# ew==  配列全体が期待される場所で、単独の数値も受け入れられる

あのブロックの中には、注目に値するエッジが 2 つあります。文字配列は、文字の数値を使って文字ごとに 1 バイトに変換されます。ラテン文字テキストなら、このトリックを使うレガシーシステムがまさに期待する形で、それ以上のものには、黙って誤ったバイトを生成します。255 を超える整数は、代わりにはっきりと失敗するエッジです:PowerShell のバイト変換は 0-255 の範囲外の値を例外で拒否するため、256 はデータをこっそり破損させる代わりに、キャストのところでスクリプトを止めます。ソースが数値なら、キャストを明示にしてください:[byte[]](1, 2, 3) は、意味する通りを正確に言っています。

メソッドに文字列を渡すと、別の理由で同じはっきりした扱いになります:文字列がどのバイトを意味するかなど知る由もないので、PowerShell の変換エンジンは諦めるからです。$null を渡すと、.NET は何かをする前に例外を投げます。どちらも正しい動作で、どちらも、第 1 節の 2 ステップパターンこそが唯一持つ価値のあるパターンである理由です。

行折り返し:76、64、そしてなし

既定では、エンコーダーはデータがどれだけ多くても長い 1 行を生成します。数キロバイトのファイルなら、それで十分です。人間が読む、メールに貼り付けられる、ソース管理の diff で比較されるデータにとって、800 万文字の壁は実務的な問題で、慣習は折り返すことです。PowerShell と、それが奉仕するプロトコルは 3 つの幅を知っており、それらは互換ではありません:

幅 期待する側 行末
76 文字 MIME、メール、ほとんどのテキスト転送。InsertLineBreaks の既定 CRLF
64 文字 PEM ファイル:証明書、秘密鍵、-----BEGIN 一族の残り 慣習として LF
なし API、トークン、設定ファイル、ペイロードが機械的に処理されるものすべて 行がそもそもない

組み込みの折り返しは 1 パラメータの変更で、メール風のペイロードに欲しいのはこれです:

$text = "The quick brown fox jumps over the lazy dog. Base64 output arrives wrapped at different widths depending on who is reading it."
$wrapped = [System.Convert]::ToBase64String(
  [System.Text.Encoding]::UTF8.GetBytes($text),
  [Base64FormattingOptions]::InsertLineBreaks)
# 1 行 76 文字、行間は CRLF、MIME が期待する通り

組み込みの例外が PEM です。OpenSSL と -----BEGIN エコシステム全体が 64 文字で折り返し、その幅を生成する .NET のフラッグは存在しないからです。ループは短く、標準的なレシピです:

$der = [System.IO.File]::ReadAllBytes("./certificate.der")
$b64 = [System.Convert]::ToBase64String($der)
$lines = for ($i = 0; $i -lt $b64.Length; $i += 64) {
  $b64.Substring($i, [Math]::Min(64, $b64.Length - $i))
}
$pem = @("-----BEGIN CERTIFICATE-----") + @($lines) + @("-----END CERTIFICATE-----")
Set-Content -Path "./certificate.pem" -Value ($pem -join "`n")

幅がそもそも重要である理由は、Base64 の 4 文字グループは改行を尊重しないため、デコーダーが改行を完全に無視するかもしれませんし、強制するかもしれません。このサイトが使っているデコーダーはそれを無視しますが、厳格な消費側 - 証明書の世界やメールの世界には大勢います - は予期しない改行を異質な文字として扱い、ペイロードを拒否します。幅を選ぶとき、あなたは消費側との契約を結んでいるので、スクリプトに誰と契約しているかを名指しするコメントを書く価値があります。

base64url: 2 つの文字と、パディングの決断

標準 Base64 のプラスとスラッシュは、URL の内側ではパーセントエンコーディングを通過してはじめて合法になり、等号のパディングはフィールドの区切り文字のように読めます。そこで RFC 4648 は、URL とファイル名に安全なアルファベットを定義しました:同じ 64 文字で、ただしプラスがハイフンに、スラッシュがアンダースコアになり、パディングは通常、データの長さから不要になるため落とされます。あなたがこれまで扱ったすべての API トークンと JWT は、この標準が base64url と呼ぶべきだと主張し、単に base64 とは呼ばない、この変形で書かれています。

PowerShell の標準エンコーダーは標準アルファベットを生成するため、base64url への変換は 2 回の文字スワップと、パディングの決断です:

$bytes = [System.Text.Encoding]::UTF8.GetBytes("Париж encoded 大阪")
$standard = [System.Convert]::ToBase64String($bytes)
$standard
# 0J/QsNGA0LjQtiBlbmNvZGVkIOWkp+mYqg==  標準アルファベット、パディング込み
$url = $standard.Replace("+", "-").Replace("/", "_").TrimEnd("=")
$url
# 0J_QsNGA0LjQtiBlbmNvZGVkIOWkp-mYqg  URL 安全な形、パディング除去済み

base64url の世界でパディングを除去しても安全なのは、消費側が文字列の長さからパディングが何だったかを再計算するからです。ただし、どこでもそうとは限りません。だから決断を明示にしてください:トークン、JWT セグメント、URL 埋め込みにはパディングを落とし、厳格な標準アルファベットの消費側へ渡すものには残し、どちらを選んだかを記録しておいてください。.NET ランタイムには、このアルファベット専用のクラス、System.Buffers.Text.Base64Url(.NET 9 で追加)が同梱されており、メソッドは ReadOnlySpan<T> パラメータのまわりに構築されています。現行の PowerShell(7.4 以降で、このクラスを同梱する .NET 版本の上で実行されていれば)は、実際にこれらを直接呼び出せます - メソッドバインダーが今や配列引数を span に暗黙に変換するため、[System.Buffers.Text.Base64Url]::EncodeToString($bytes) は今日動きます - ただし、スクリプトが Windows PowerShell 5.1、古い PowerShell 7.x リリース、.NET 9 以前のランタイムのホストで動かなければならない場合、手が伸びるべきは依然として 2 文字のスワップで、それらのすべての版本で動作します。

JWT の鋳造

JSON Web Token は、base64url の本番世界における旗艦級の用途であり、エンコーディングパイプラインの良い完全なテストでもあります。JWT はドットで結ばれた 3 つのエンコード済みセグメント - ヘッダー、ペイロード、署名 - だからです。最初の 2 つは base64url によるコンパクト JSON で、3 つ目は最初の 2 つの正確なテキストに対するハッシュのバイナリ出力です。PowerShell で頭から尾まで構築した、完全な HS256 トークンがここにあります:

$header = @{ alg = "HS256"; typ = "JWT" } | ConvertTo-Json -Compress
$payload = @{ sub = "1234567890"; name = "John Doe"; iat = 1516239022 } | ConvertTo-Json -Compress
function UrlEncode64([byte[]]$bytes) {
  $standard = [System.Convert]::ToBase64String($bytes).TrimEnd("=")
  return $standard.Replace("+", "-").Replace("/", "_")
}
$left = (UrlEncode64 ([System.Text.Encoding]::UTF8.GetBytes($header))) + "." + (UrlEncode64 ([System.Text.Encoding]::UTF8.GetBytes($payload)))
$hmac = [System.Security.Cryptography.HMACSHA256]::new([System.Text.Encoding]::UTF8.GetBytes("secret"))
$signature = UrlEncode64 ($hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($left)))
$jwt = $left + "." + $signature
# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MTYyMzkwMjIsInN1YiI6IjEyMzQ1Njc4OTAiLCJuYW1lIjoiSm9obiBEb2UifQ.6MWZy9doHbfyomJd4soTRUQft7PmRM2EyxxT3SLoiyE
#  PowerShell 7.4 での出力。セグメント内部のキー順 - したがって署名も - は版本によって異なる場合がある

署名セグメントを見てください:base64url アルファベットの列で、プラスがハイフンとして、スラッシュがアンダースコアとして現れる、あのアルファベットです。あの例についての 3 つのことを知っておくと、本番インシデントから身を守れます。1 つ目:署名は、キー順と空白を含む正確な JSON テキストに対して計算されるため、あなたが署名する JSON と、検証に使う JSON は、バイト単位で完全に同じでなければなりません。PowerShell の ConvertTo-Json はキー順をあなたに代わって決めますが、それはあなたが制御できるものではないため、トークンのセグメントを手で並べ替えたり、署名とチェックの間にフォーマットし直したりしないでください。2 つ目:-Compress は化粧ではありません:ヘッダーやペイロードに空白が 1 つでも入ったトークンは、標準形はコンパクトだから、準拠した実装では決して検証に通りません。3 つ目:タイムスタンプ iat は Unix エポックからの秒数で、変換せずに Get-Date から作ったペイロードは、年単位で範囲を外れます。デコードの方向、つまり誰かが鋳造したトークンの中を覗く話は、姉妹サイトの関連記事で扱っています。

ファイルとバイトストリーム

ファイルはすべての中で最も一般的なペイロードで、パイプラインは短いです。ファイルをバイトとして読み、エンコードし、テキストを書き込む。重要な 2 行は、読み取り - これはバイト読みでなければならず - と書き込み - 通常、末尾の改行を追加してはならず - です:

$bytes = [System.IO.File]::ReadAllBytes("./photo.png")
$encoded = [System.Convert]::ToBase64String($bytes)
Set-Content -Path "./photo.b64" -Value $encoded -NoNewline
$encoded.Length
# 出荷しようとしているテキストのサイズ

実務的な注記を 2 つ。1 つ目は計算の話:Base64 はすべてを大きくし、10 メガバイトのファイルなら、出荷するテキストは約 13.4 メガバイトになります。転送にサイズ制限がある、あるいはこのテキストがメール本文や URL に入るなら、エラーの後でなく、エンコードする前に計算してください。2 つ目は末尾の改行:Set-Content は既定で 1 つ追加します。このサイトが使っているデコーダーやほとんどのモダンなデコーダーはそれを無視しますが、一部の厳格な消費側は無視しません。-NoNewline はあなたに何もコストをかけず、その疑問を除去します。

PowerShell 6 以降は、言語の中で完結する 2 つ目の読み取りを提供します:Get-Content -AsByteStream -Raw はファイルを単一のバイト配列として 1 回の呼び出しで返し、.NET の ReadAllBytes とは別の整理された代替手段で、この目的では同一の挙動をします。Windows PowerShell 5.1 には -AsByteStream がなく、.NET の読み取りが唯一の選択肢で、それはシェルのすべての版本で同じ挙動をするものです。

証明書:PEM と PFX からテキストへ

日常の運用で、証明書はエンコーディングの市民の中で最も重い存在です。デプロイはテキストとしてそれらを運ぶのが好きだからです。PEM 証明書は、アーマー行の間で折り返された Base64 のボディで、折り返しセクションのレシピがエクスポート全体のすべてです:

$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new([System.IO.File]::ReadAllBytes("./certificate.der"))
$cert.Subject
# CN=example.org
$b64 = [System.Convert]::ToBase64String($cert.RawData)
# 証明書のバイナリ形式の長い 1 行

PFX 形式はもう一方の主力です:証明書とその秘密鍵を 1 つのバイナリファイルに収めたもの。それが、デプロイスクリプトや設定ストアの中で Base64 テキストとして放置されているのを最もよく見かける形式である理由です。1 つをエンコードするのは、前のセクションの素のファイルパイプラインで、PowerShell 7 での読み取り方向は 1 つの cmdlet の話です:

$pfxBytes = [System.IO.File]::ReadAllBytes("./certificate.pfx")
$pfxB64 = [System.Convert]::ToBase64String($pfxBytes)
# バンドルのテキスト形式、設定ファイルへそのまま
Get-PfxCertificate -FilePath "./certificate.pfx" -Password (ConvertTo-SecureString "secret" -AsPlainText -Force)
# 生きた証明書、手動デコード不要

セキュリティについて、1 文。Base64 は逆の仮定を誘発するので、率直に言います:Base64 の PFX は、テキストとしての秘密鍵です。エンコーディングはシークレットの形を変えるだけで、その秘匿性は何も変えないため、チャットウィンドウ、チケット、コミットに貼り付けられた Base64 の PFX は、チャットウィンドウ、チケット、コミットに貼り付けられた秘密鍵です。テキスト形式には、バイナリ形式が受けるのとまったく同じ注意を払い、どちらかを選ぶなら、証明書ストアかシークレットマネージャを優先しましょう。

Basic 認証、Data URI、そして古い習慣

Base64 は、それの名前をつけた標準文書より古いのです。1996 年の MIME 一族の RFC 群がそれをメールに乗せ、HTTP Basic 認証がそれを初期ウェブのすべてのヘッダー交換に乗せました。クライアントはそこで今も、認証情報のペアを 1 つの Base64 文字列としてエンコードしています:

$credential = [System.Text.Encoding]::UTF8.GetBytes("alice:s3cret!")
[System.Convert]::ToBase64String($credential)
# YWxpY2U6czNjcmV0IQ==
# 送信される形:Authorization: Basic YWxpY2U6czNjcmV0IQ==

ここでの正しいのは、プラスとスラッシュを含む標準アルファベットです。ヘッダーは URL ではなく、安全なアルファベットを必要としないからです。同じ仕組みは Data URI にも現れます:ドキュメントが自分のバイナリをインラインで埋め込む方法で、その形はリテラルな接頭辞と、バイトの標準 Base64 です:

$dataUri = "data:application/octet-stream;base64," + [System.Convert]::ToBase64String([byte[]](1, 2, 3, 250, 251))
# data:application/octet-stream;base64,AQID+vs=

この 2 つの習慣は、あなたが作るものとしてよりも、出会うものとして知る価値があります:ヘッダーやリンクに長い Base64 の列が現れたら、まずチェックすべきはこれらの 2 つの形式で、どちらも言っているものは、1 回の素のデコードの先にあります。それが形式の要点であり、このサイトのデコード側が存在する理由です。

エンコードされたコマンドと Windows の道具箱

PowerShell はバージョン 1.0 以来、エンコードするための組み込みの理由を携えていました:ホスト自体の -EncodedCommand パラメータです。pwsh に Base64 文字列を手渡すと、バイト列を UTF-16LE としてデコードし、その結果がコマンドとして実行されます。ドキュメントに記載されている目的は、外側のシェルのクォーティングと戦うコマンドであり、エンコード側は 2 行です:

$command = "Write-Host 'Hello from the encoded side'"
$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command))
# VwByAGkAdABlAC0ASABvAHMAdAAgACcASABlAGwAbABvACAAZgByAG8AbQAgAHQAaABlACAAZQBuAGMAbwBkAGUAZAAgAHMAaQBkAGUAJwA=
pwsh -NoProfile -EncodedCommand $encoded
# Hello from the encoded side

エンコード行をよく読んでください。誰もがそれを誤るからです:ペイロードは UTF-16LE でなければならず、それは Unicode エンコーディングで、UTF-8 ではありません。間違えた方でエンコードすると、ホストはどうあれあなたのバイトを UTF-16LE としてデコードし、文字化けでできたコマンドを実行し、その間違いの完璧な肖像画となるエラーを生成します。デコード記事はその失敗を完全に扱い、この側の修正は 1 つの単語です:Unicode。

言語の外側では、ネイティブなツールはそれぞれ、静かなエンコーディング判断を内包しています。Windows では、certutil -encode infile outfile.b64 は PEM が期待するアーマー行付きの標準 Base64 ファイルを生成し、-f は既存の出力を上書きし、覚えておく価値のあるフラッグは -unicodetext で、certutil に出力ファイルを Unicode で書き出させます(Microsoft のドキュメントによれば:「出力ファイルを Unicode で書き込みます」)- エンコーディング判断を隠す 1 つのスイッチです。Linux では古典的なユーティリティが base64 -w 0 file で、ここで -w 0 が荷重を支える部分です:それがないと GNU base64 は 76 文字で折り返し、1 行を望んだのに MIME 風のファイルを手にすることになります。macOS の BSD フレーバーにはそうしたフラッグは不要です。既定で途切れない 1 行を出力するからです。

出力が巨大なときのエンコード

日常のサイズなら、全部読んで全部エンコードするパイプラインが速くシンプルな方法で、ファイルがメモリに快適に保持できるサイズを超えて、データがダウンロードやソケットから少しずつ届くようになるまでは、それが正しい方法です。そこでドキュメントに記載されているツールは、ストリーミングのペアです:CryptoStream に包まれた System.Security.Cryptography.ToBase64Transform。生のバイトを書き込み、Base64 テキストが出てくる構造で、どの瞬間にも生きているのは小さなバッファだけです:

$source = [System.IO.File]::OpenRead("./photo.png")
$destination = [System.IO.File]::Create("./photo.b64")
$transform = [System.Security.Cryptography.ToBase64Transform]::new()
$stream = [System.Security.Cryptography.CryptoStream]::new($destination, $transform, [System.Security.Cryptography.CryptoStreamMode]::Write)
$buffer = New-Object byte[] 65536
while (($read = $source.Read($buffer, 0, $buffer.Length)) -gt 0) {
  $stream.Write($buffer, 0, $read)
}
$stream.Dispose()
$source.Dispose()
$destination.Dispose()

1 発メソッドとの 1 つの違いは、書き留める価値があります:ストリームは、入力がどれだけ大きくなっても、折り返しのない連続した 1 行を生成します。空白を無視するデコーダーはそれを気にしませんが、最終的な目的地が PEM ファイルなら、結果に折り返しセクションの 64 桁ループを後から回してください。そして C# では標準形は、C# 記事で見るのと同じ ToBase64Transform + CryptoStream パターンで、PowerShell は上記のようにそれを直接駆動します。

エンコードされたペイロードが迷う場所

  • バイトではなく文字列をエンコードする。ToBase64String("Hello") は変換例外を投げますが、それはメソッドが 1 つ目の判断、つまりエンコーディングがなされていないことを伝えているのです。スクリプトの中でそれを目に見えるにすると、エラーは消えます。
  • UTF-8 と約束された場所に UTF-16。ペイロードは期待の 2 倍の長さになり、消費側はゴミを読みます。エンコーディングはプロトコルの一部で、現代のインターネットのほぼすべてのワイヤーで、プロトコルは UTF-8 と言っています。
  • 5.1 の読み取り。Windows PowerShell 5.1 は、スクリプトが見る前に、BOM なしのテキストファイルをそのマシンの ANSI コードページで読むため、UTF-8 のソースファイルはエンコーディングステップの前に破損することがあります。5.1 では、明示的な UTF-8 読み取りでテキストを読み、結果の最初の文字を確認してください。
  • 間違った折り返し幅。MIME は 76 を、PEM は 64 を、API はなしを求め、厳格な消費側は予期しない改行を異質な文字として扱います。消費側から幅を選び、コメントにそう書いておいてください。
  • スワップの悪い側にパディング。等号を落とすのは base64url トークンに対しては正しく、標準パディングを期待する消費側に対しては誤りです。アルファベットの交換とパディングの決断は、1 つの選択ではなく 2 つの選択です。
  • 署名したものを整形し直す。JWT の署名は、正確な JSON テキスト、キー順と空白も含めてカバーします。クレームの順序を変えたり、空白を 1 つ加えたりすると、トークンは検証に通りなくなり、原因の近くにエラーメッセージはつきません。
  • 255 を超える値。整数をバイトに変換すると、ラップする代わりに例外を投げるため、256 はキャストのところでスクリプトを止めます。ソースデータが数値なら、キャストを明示にし、誤りをあなたが目にする誤りのままにしてください。
  • 仮装を信じる。Base64 は暗号化でも圧縮でもなく、データを 1/3 増やす翻訳です。Base64 のシークレットは平文のシークレットであり、Base64 のファイルは 33% 多くの場所が必要なファイルです。

信頼できるエンコーダーのためのルール

  • 意図的にバイトを生成する。エンコードスクリプトの 1 行目は、明示的な GetBytes かバイト読みでなければなりません。PowerShell が文字列を正しいバイトに変換してくれるという期待では決してありません。
  • 判断を下すコードの隣のコメントに、アルファベットと幅の名前を付ける:標準か base64url か、76 で、64 で、あるいは折り返さないか。消費者は 6 か月後にスクリプトを読む人で、その人はあなたです。
  • 消費側が末尾の改行を明確に期待しない限り、テキストファイルは -NoNewline で書き、行末(LF か CRLF)は消費側のドキュメントが期待する通りに選びます。
  • 構築しながら往復をテストする:エンコード、デコード、バイトを比較。2 つのバイト配列に対する 30 秒の Compare-Object が、原因がまだ新鮮なうちに、エンコーディングの間違い、折り返しの間違い、バイト順の間違いをまとめて掴みます。
  • ペイロードではなく、サイズをログに出す。前のバイト数と後の文字数は、約 1.33 の比率で落ち着くべきで、そうでないとき、サイズのはずれは、ログにデータが含まれることなく、どこを見るべきかを教えてくれます。

PowerShell がエンコーダーを継いだ経緯

PowerShell における Base64 エンコードの、最も短い本当の歴史は、PowerShell はそれを一度も書いたことがない、というものです。あなたが呼び出すメソッド、Convert.ToBase64String は 2003 年に .NET Framework 1.1 とともに登場し、2006 年 11 月のバージョン 1.0 以降のすべての PowerShell は、単に自分が動かしている .NET を公開しているだけです。このプロジェクトは開発中は Monad と呼ばれ、2003 年 10 月の Professional Developers Conference で初めて一般公開されました。リリースの頃には、PowerShell が包む .NET エンコーダーはすでに 3 年の歳月を経て、ウェブトラフィックを運んでいました。

形式は、シェルが登場した同じ年に標準化されました。2006 年 10 月に公開された RFC 4648 が、アルファベット、パディング規則、デコードの厳格さ、base64url 変形を固定し、.NET のペアが実装している動作を、今もまさにそのまま記述しています。それより前に 1996 年に来た MIME RFC 群は、すでに 76 文字の折り返しをメールに乗せており、それが今日まで InsertLineBreaks の既定の幅である理由です。PowerShell が 2016 年 8 月に PowerShell Core としてオープンソース化・クロスプラットフォーム化するとき、エンコーダーは何の変更もなしに Linux と macOS に同行しました。変えるものが何もないからです。

後に変わったのは .NET で、たいていは PowerShell の手の届かないところで起きました。ランタイムは最近の版本で、より速い span ベースの Base64 ヘルパー(Base64Url クラスと Try 接頭辞のデコードメソッドを含む)を得ました。span は byref 風型で、古い PowerShell リリースは本当にそれらにバインドできませんでした。しかし現行 PowerShell のメソッドバインダーは、暗黙の配列から span への変換を今や実行するため、十分に新しいホストのスクリプトからこれらのショートカットを呼び出せます。それより古いすべてに対するコミュニティの答えは、PowerShell Gallery からの Microsoft.PowerShell.TextUtility モジュールで、その ConvertTo-Base64 は同じ .NET メソッドを包み、UTF-8 既定の -Text パラメータと、76 桁の折り返し用の -InsertBreakLines スイッチを追加しています。cmdlet の形を好むなら Install-Module -Name Microsoft.PowerShell.TextUtility でインストールしてください。なお、このモジュールはアーカイブされており、アクティブに維持されていません。これがもう 1 つの理由で、組み込みメソッドが新規スクリプトへの推奨を続けているのです。

覚えておくべき数字と名前

  • 入力バイト 3 つが出力 4 文字になるので、エンコードされたデータは元より約 33% 大きくなり、パディングは等号 2 つを超えることはありません。
  • 既定の出力は途切れない 1 行です。InsertLineBreaks は 1996 年の MIME の慣習に従い、CRLF で 76 文字ごとに折り返します。PEM は 64 を求め、その幅を生成する組み込みフラッグはありません。
  • base64url は、プラスとスラッシュをハイフンとアンダースコアに置き換えた標準 Base64 で、パディングは通常落とされ、すべての JWT と API トークンのアルファベットです。
  • 「Café」は UTF-8 では 5 バイト、UTF-16LE では 8 バイトです。同じに見えるテキスト、2 倍のサイズ、そしてこの 2 つのエンコーディングは、ワイヤーの上では互換ではありません。
  • -EncodedCommand は最初の PowerShell リリースから存在し、そのペイロードは UTF-8 ではなく UTF-16LE でなければなりません。この側の最も一般的な間違いを直す 1 つの単語は、Unicode です。
  • certutil -encode は -unicodetext の中にエンコーディング判断を隠し、GNU base64 は 76 桁の折り返しではなく 1 行を渡すために -w 0 が必要です。
  • .NET の span ベースの Base64 ヘルパー(Base64Url を含む)は、一度 PowerShell から到達不能でした。span は古いメソッドバインダーがバインドできなかった byref 風型だからです。現行の PowerShell(7.4 以降、そのクラスを同梱できるほど新しい .NET ランタイム)は、配列引数を span パラメータに対して文句なく解決するので、今日の直接呼び出しは動きます - ただし、2 文字のスワップは、古いものも新しいものも、すべての版本で動作する唯一のレシピです。
  • 単独のバイト 123 は ew== にエンコードされます:出力の長さが入力の長さを教えてくれる、というルールの、考えられる最小の例です。

矢印を反転させて

この記事のすべては、あなたが保持するデータを、Base64 文字列に変えることについてです。鏡像の操作、つまり文字列を取り、あなたのデータを元に戻す方には、独自の登場人物たちがいます:4 種類の空白を無視するデコーダー、3 つの罪を 1 つのエラーメッセージでカバーするもの、覗き込む JWT、ほどく証明書、そして説明する -EncodedCommand。その方向は、姉妹サイトの関連記事「PowerShell での Base64 デコード」の中で、下記のリンクから、独自の罠と独自の歴史を携えて、独自の完全な扱いを受けます。

最終更新: 2026-10-10

関連記事: PowerShell での Base64 デコード:完全ガイド