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

C# での Base64

System.Convert は .NET Framework 1.0 から Base64 に対応しており、.NET 9 では URLセーフな文字列向けに System.Buffers.Text.Base64Url が追加されました。以下の出力は .NET 9 のものです。

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

Convert はバイト列を扱うので、エンコーディングは明示的に選んでください。プロセスの外に出るデータなら、ほぼ必ず UTF-8 です。

using System.Text;

string enc = Convert.ToBase64String(Encoding.UTF8.GetBytes("héllo wörld"));
Console.WriteLine(enc);   // aMOpbGxvIHfDtnJsZA==

string dec = Encoding.UTF8.GetString(Convert.FromBase64String(enc));
Console.WriteLine(dec);   // héllo wörld

Encoding.Unicode は UTF-16LE で、ASCII テキストのサイズが 2 倍になります。これが正しいのは、PowerShell のエンコードされたコマンド 形式のように、相手が UTF-16 を想定している場合だけです。

実際によく使うのが、HTTP の Basic 認証ヘッダーです。

string token = Convert.ToBase64String(Encoding.UTF8.GetBytes("user:pass"));
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", token);
// Authorization: Basic dXNlcjpwYXNz

Basic Auth ヘッダージェネレーター を使えば、同じ値をブラウザで生成できます。

URLセーフな Base64:.NET 9 の Base64Url

.NET 9 で System.Buffers.Text.Base64Url が追加されました。エンコード時はパディングを付けず、デコード時はパディングの有無を問いません。

using System.Buffers.Text;

byte[] d = Encoding.UTF8.GetBytes("<<???>>");
Console.WriteLine(Base64Url.EncodeToString(d));            // PDw_Pz8-Pg

byte[] a = Base64Url.DecodeFromChars("PDw_Pz8-Pg");        // <<???>>
byte[] b = Base64Url.DecodeFromChars("PDw_Pz8-Pg==");      // <<???>>

標準アルファベットは拒否します。Base64Url.DecodeFromChars("PDw/Pz8+Pg") は FormatException をスローします。アロケーションを避けられる Span ベースのオーバーロード(EncodeToChars、DecodeFromUtf8、TryDecodeFromChars)もあります。

Microsoft.Bcl.Memory パッケージのない古いランタイムでは、手動で変換します。

string url = Convert.ToBase64String(d)
    .TrimEnd('=').Replace('+', '-').Replace('/', '_');    // PDw_Pz8-Pg

デコードには逆の置き換えと、4 の倍数になるまでのパディングが必要です。

static byte[] FromBase64Url(string s)
{
    s = s.Replace('-', '+').Replace('_', '/');
    s = s.PadRight(s.Length + (4 - s.Length % 4) % 4, '=');
    return Convert.FromBase64String(s);
}

Encoding.UTF8.GetString(FromBase64Url("PDw_Pz8-Pg"));   // <<???>>

ASP.NET Core のプロジェクトなら、代わりに Microsoft.AspNetCore.WebUtilities の WebEncoders.Base64UrlEncode を使えます。2 つのアルファベットについて詳しくは Base64 と Base64URL の違い をご覧ください。

例外を使わずに入力を検証する

Convert.FromBase64String は不正な入力に対して System.FormatException をスローします。.NET 9 では、ほぼどのケースでも同じメッセージです。

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.

このメッセージは、PDw_Pz8-Pg==(URLセーフ)、aGk=!!(ゴミ文字)、aGk(パディングなし)、a、aGk== のいずれでも表示されました。一方、空白(" aGk= " と "aGVs\nbG8K" は問題なくデコードされます)や、0 でないパディングビット(aGl=)は受け付けます。

ホットパスで例外を避けたいなら、バッファを渡す Try 版か、Base64.IsValid(.NET 8 以降)を使ってください。

Span<byte> buf = stackalloc byte[16];
Convert.TryFromBase64String("aGk=", buf, out int n);   // True, n = 2
Convert.TryFromBase64String("aGk", buf, out n);        // False, n = 0

System.Buffers.Text.Base64.IsValid("aGk=");      // True
System.Buffers.Text.Base64.IsValid("aGk");       // False
Base64Url.IsValid("PDw_Pz8-Pg");                 // True

バッファはデコード結果が収まる大きさが必要で、足りないと有効な入力でも TryFromBase64String が false を返します。サイズは (s.Length * 3) / 4 にしてください。デコードに失敗する文字列には、Base64 バリデーター と エラーの解説 が役立ちます。

改行(MIME)

Base64FormattingOptions.InsertLineBreaks を指定すると、出力を \r\n で 76 文字ごとに折り返します。末尾には改行が付きません。

string mime = Convert.ToBase64String(
    Encoding.ASCII.GetBytes(new string('x', 60)),
    Base64FormattingOptions.InsertLineBreaks);
// "eHh4eHh4...eHh4" (76 chars) + "\r\n" + "eHh4"

FromBase64String は、折り返された形式をそのまま受け付けます。MIME Base64 エンコーダー も同じレイアウトで出力します。

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

Console.WriteLine(Convert.ToBase64String(File.ReadAllBytes("hello.txt")));  // aGVsbG8K
File.WriteAllBytes("copy.txt", Convert.FromBase64String("aGVsbG8K"));

CryptoStream で大きなファイルを扱う

ToBase64Transform と FromBase64Transform は CryptoStream に組み込めるので、メモリ使用量が一定に保たれます。名前空間はそれらしいですが、暗号処理は一切行われません。

using System.Security.Cryptography;

using (var input = File.OpenRead("big.bin"))
using (var output = File.Create("big.b64"))
using (var b64 = new CryptoStream(output, new ToBase64Transform(), CryptoStreamMode.Write))
{
    input.CopyTo(b64);
}

using (var input = new CryptoStream(File.OpenRead("big.b64"),
                                    new FromBase64Transform(), CryptoStreamMode.Read))
using (var output = File.Create("big.out"))
{
    input.CopyTo(output);
}

CryptoStream を Dispose すると最後のブロックとパディングが書き出されます。これを省くと出力が途中で切れます。300 KB のテストファイルでは、両方向とも、メモリ上で変換した結果と一致しました。手早く確認したいときは、結果を Base64 からファイルへの変換 で開いてください。

よくある落とし穴

  • Data URI の接頭辞。 data:image/png;base64, には : と ; が含まれ、FormatException の原因になります。カンマまでをすべて取り除いてください。
  • URLセーフなトークンを Convert に渡す。 JWT の各パートは上記の汎用メッセージで失敗します。Base64Url か JWT デコーダー を使ってください。
  • Encoding.Default。 .NET Framework では ANSI コードページ、.NET Core 以降では UTF-8 です。両者の間でコードを移すと、非 ASCII テキストの出力が変わります。
  • Base64 は暗号化ではありません。 Base64 で「保護」された接続文字列は、誰が見ても平文と同じです。エンコードと暗号化の違い をご覧ください。

バージョンごとの注意

  • .NET Framework 1.0 以降:Convert.ToBase64String、FromBase64String、ToBase64Transform
  • .NET Core 2.1 以降:Span 版オーバーロードの Convert.TryFromBase64String、TryToBase64Chars、UTF-8 用の System.Buffers.Text.Base64 クラス
  • .NET 8:Base64.IsValid
  • .NET 9:Base64Url(古いターゲットでも Microsoft.Bcl.Memory 経由で利用可能)

よくある質問

「The input is not a valid Base-64 string」を直すには?

URLセーフの文字(-、_)、パディングの不足、Data URI の接頭辞がないか確認してください。Convert.FromBase64String が受け付けるのは、正しくパディングされた標準アルファベットだけです。トークンには .NET 9 の Base64Url.DecodeFromChars を使えば、パディングの有無にかかわらずデコードできます。

.NET 8 や .NET Framework でも Base64Url を使えますか?

Base64Url は .NET 9 以降に組み込まれています。それより古いターゲット向けには、Microsoft が同じ型を Microsoft.Bcl.Memory パッケージで提供しています。パッケージを使わない場合は、標準の出力を TrimEnd('=') と 2 回の Replace で変換してください。

テキストの Base64 に Encoding.Unicode を使うのが間違いなのはなぜですか?

Encoding.Unicode は UTF-16LE なので、hi が aGk= ではなく aABpAA== になります。PowerShell の -EncodedCommand のように受け取り側が UTF-16 を想定している場合を除き、Encoding.UTF8 を使ってください。

続けて読む