Загрузить файл
  1. base64hub
  2. Base64 в коде
  3. C#
Код

Base64 в C#

System.Convert умеет Base64 со времён .NET Framework 1.0, а в .NET 9 появился System.Buffers.Text.Base64Url для URL-safe строк. Результаты ниже получены на .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-текста. Он подходит, только когда другая сторона ждёт UTF-16, например в формате закодированной команды PowerShell.

Частый практический случай: заголовок HTTP Basic Auth.

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

Генератор заголовка Basic Auth выдаёт такое же значение прямо в браузере.

URL-safe Base64: Base64Url в .NET 9

В .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 можно вместо этого взять WebEncoders.Base64UrlEncode из Microsoft.AspNetCore.WebUtilities. Подробнее о двух алфавитах в статье 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-safe), aGk=!! (мусор), aGk (нет дополнения), a и aGk==. Пробелы метод допускает (" aGk= " и "aGVs\nbG8K" декодируются нормально), как и ненулевые биты дополнения (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 переносит вывод через каждые 76 символов с \r\n, без перевода строки в конце:

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 сбрасывает последний блок и дополнение; без него вывод окажется обрезанным. На тестовом файле в 300 КБ оба направления совпали с результатом преобразования в памяти. Для быстрой ручной проверки откройте результат инструментом Base64 в файл.

Частые подводные камни

  • Префиксы data URI. В data:image/png;base64, есть : и ;, на которых возникает FormatException. Отрежьте всё до запятой включительно.
  • URL-safe токены в 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 и класс System.Buffers.Text.Base64 для UTF-8.
  • .NET 8: Base64.IsValid.
  • .NET 9: Base64Url, для старых платформ также доступен через Microsoft.Bcl.Memory.

Вопросы и ответы

Как исправить ошибку «The input is not a valid Base-64 string»?

Проверьте, нет ли во входных данных URL-safe символов (-, _), не пропущено ли дополнение и нет ли префикса data URI. Convert.FromBase64String принимает только стандартный алфавит с правильным дополнением. Для токенов в .NET 9 берите Base64Url.DecodeFromChars: он принимает строки и с дополнением, и без.

Можно ли использовать Base64Url в .NET 8 или .NET Framework?

Base64Url встроен в .NET 9 и новее. Для более старых платформ Microsoft поставляет тот же тип в пакете Microsoft.Bcl.Memory. Без него преобразуйте стандартный результат через TrimEnd('=') и два вызова Replace.

Почему Encoding.Unicode не подходит для Base64 от текста?

Encoding.Unicode – это UTF-16LE, поэтому hi превращается в aABpAA== вместо aGk=. Используйте Encoding.UTF8, если только получатель не ждёт UTF-16, как -EncodedCommand в PowerShell.

Читайте также