Base64 Decode C# — Руководство Convert.FromBase64String()

·Game Developer & Unity Engineer·ПровереноEmma Richardson·Опубликовано

Используйте бесплатный Base64 Decode Online прямо в браузере — установка не требуется.

Попробовать Base64 Decode Online онлайн →

В каждом .NET-проекте, с которым я работал, рано или поздно возникала необходимость декодировать Base64 — извлечение строк подключения из секретов Kubernetes, чтение бинарных данных из веб-хуков или просмотр JWT-токенов во время отладки. Для декодирования Base64 в C# основным методом является Convert.FromBase64String(), который возвращает byte[], передаваемый затем в Encoding.UTF8.GetString() для получения читаемого текста. Для быстрой проверки без написания кода декодер Base64 от ToolDeck обрабатывает данные мгновенно прямо в браузере. В этом руководстве рассматриваются Convert.FromBase64String(), основанный на Span метод TryFromBase64String() для .NET 5+, высокопроизводительный API System.Buffers.Text.Base64, извлечение полезной нагрузки JWT, декодирование файлов и ответов API, потоковая обработка с CryptoStream и четыре наиболее распространённые ошибки разработчиков C#.

  • Convert.FromBase64String(s) + Encoding.UTF8.GetString(bytes) — стандартный двухшаговый конвейер, работающий во всех версиях .NET.
  • Convert.TryFromBase64String() избегает исключений при невалидных входных данных и записывает результат в Span<byte> — идеально для горячих путей выполнения в .NET 5+.
  • System.Buffers.Text.Base64.DecodeFromUtf8() обеспечивает декодирование без выделения памяти для UTF-8 байтовых буферов в высоконагруженных сервисах.
  • JWT-токены используют Base64url (- и _ вместо + и /) — необходимо нормализовать входные данные перед вызовом Convert.FromBase64String().
  • CryptoStream с FromBase64Transform обрабатывает потоковое декодирование больших файлов без загрузки всего содержимого в память.

Что такое декодирование Base64?

Кодирование Base64 преобразует бинарные данные в 64-символьный ASCII-алфавит, чтобы они могли передаваться через текстовые каналы — JSON-поля, HTTP-заголовки, тела писем, XML-атрибуты. Каждые 3 байта входных данных превращаются в 4 символа Base64, вот почему вывод Base64 всегда примерно на 33% больше оригинала. Декодирование выполняет обратное преобразование. Символ дополнения = в конце сообщает декодеру, сколько байт нужно отсечь из последней группы. Один символ = означает, что в последнем блоке было 2 байта; == означает 1 байт. Base64 — это не шифрование: любой может обратить это преобразование. Цель — безопасная передача через каналы, которые искажают сырые бинарные данные, а не обеспечение конфиденциальности.

Before · text
After · text
cmVkaXM6Ly9jYWNoZS1wcm9kLmludGVybmFsOjYzNzkvc2Vzc2lvbi1zdG9yZQ==
redis://cache-prod.internal:6379/session-store

Convert.FromBase64String() — стандартный метод декодирования

Метод Convert.FromBase64String() присутствует в .NET начиная с версии Framework 1.1. Никаких NuGet-пакетов, никаких дополнительных импортов помимо System — просто вызовите его и получите обратно byte[]. Двухшаговый конвейер декодирования Base64 в строку C# всегда одинаков: Convert.FromBase64String() для получения байт, затем Encoding.UTF8.GetString() для интерпретации этих байт как текста. Сложность в том, что метод возвращает сырые байты, а не строку. Нужно выбрать правильную кодировку Encoding для преобразования байт обратно в текст, и этот выбор имеет большее значение, чем кажется. Несоответствие кодировки молча приводит к мусорным символам — без какого-либо исключения в качестве предупреждения.

Минимальный рабочий пример

C# (.NET 6+)
using System;
using System.Text;

// Строка подключения хранится как Base64 в секрете Kubernetes
string encoded = "cmVkaXM6Ly9jYWNoZS1wcm9kLmludGVybmFsOjYzNzkvc2Vzc2lvbi1zdG9yZQ==";

byte[] decodedBytes = Convert.FromBase64String(encoded);
string connectionString = Encoding.UTF8.GetString(decodedBytes);

Console.WriteLine(connectionString);
// redis://cache-prod.internal:6379/session-store

Всегда используйте Encoding.UTF8, если нет веской причины поступить иначе. Среда выполнения .NET представляет строки внутренне как UTF-16, но большинство данных, пересекающих границы систем (ответы API, конфигурационные файлы, секреты), закодированы в UTF-8. Использование Encoding.ASCII для данных с многобайтовыми символами молча заменяет их на ? — без исключения, просто испорченный вывод.

Проверка обратной совместимости

C# (.NET 6+)
using System;
using System.Text;

string original = "postgres://db-admin:Kx8!mQ@db-prod.us-east-1.internal:5432/orders";

// Кодирование
string encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(original));
Console.WriteLine(encoded);
// cG9zdGdyZXM6Ly9kYi1hZG1pbjpLeDghbVFAZGItcHJvZC51cy1lYXN0LTEuaW50ZXJuYWw6NTQzMi9vcmRlcnM=

// Декодирование
byte[] decoded = Convert.FromBase64String(encoded);
string recovered = Encoding.UTF8.GetString(decoded);

Console.WriteLine(recovered == original); // True

Выбор правильной кодировки

Кодировка Encoding, передаваемая в GetString(), должна совпадать с той, что использовалась при исходном кодировании данных. Выберете неправильную — получите мусорные символы без исключения: декодер с удовольствием выдаст бессмыслицу. Вот практическое руководство:

  • Encoding.UTF8 — безопасный вариант по умолчанию. Обрабатывает ASCII и весь Unicode. Используйте его, если не знаете точно.
  • Encoding.ASCII — только для чистых 7-битных ASCII данных. Многобайтовые символы превращаются в ?.
  • Encoding.Unicode — в .NET это UTF-16LE. Некоторые внутренние строки Windows и экспорты PowerShell используют эту кодировку.
  • Encoding.Latin1 — устаревшая западноевропейская кодировка. Встречается в старых SOAP-сервисах и мейнфреймовых интеграциях.
C# (.NET 6+)
using System;
using System.Text;

// Одни и те же байты, разные кодировки — разные результаты
byte[] decoded = Convert.FromBase64String("w7bDvMOk");

Console.WriteLine(Encoding.UTF8.GetString(decoded));    // oua  (верно)
Console.WriteLine(Encoding.ASCII.GetString(decoded));   // ??????  (потери)
Console.WriteLine(Encoding.Latin1.GetString(decoded));  // öüä  (мусор)

Вспомогательный метод декодирования с обработкой ошибок

Поскольку Convert.FromBase64String() выбрасывает исключение при некорректных входных данных, а зачистка пробелов встречается постоянно, я держу небольшой вспомогательный класс в большинстве проектов:

C# (.NET 6+)
using System;
using System.Text;

static class Base64Helper
{
    public static string? DecodeToString(string encoded)
    {
        if (string.IsNullOrWhiteSpace(encoded))
            return null;

        // Удаляем пробелы, которые декодер .NET отвергает
        string cleaned = encoded
            .Replace("
", "")
            .Replace("
", "")
            .Replace(" ", "")
            .Trim();

        try
        {
            byte[] bytes = Convert.FromBase64String(cleaned);
            return Encoding.UTF8.GetString(bytes);
        }
        catch (FormatException)
        {
            return null; // или выбрасываем доменное исключение
        }
    }
}

// Использование
string? decoded = Base64Helper.DecodeToString("  cmVkaXM6Ly9jYWNoZQ==  \n");
Console.WriteLine(decoded); // redis://cache
Примечание:Convert.FromBase64String() выбрасывает FormatException, если входные данные содержат символы за пределами алфавита Base64 — включая пробелы, переносы строк и URL-безопасные символы - и _. Приведённый выше вспомогательный метод автоматически обрабатывает пробелы.

Декодирование Base64 в нестандартные типы

Декодер всегда возвращает byte[]. Что делать с этими байтами — зависит от исходных данных. Иногда это GUID, хранящийся как 16 сырых байт, иногда — сериализованное protobuf-сообщение, иногда — временна́я метка в бинарном формате. Вот наиболее часто используемые мной преобразования.

Base64 в GUID

C# (.NET 6+)
using System;

// Некоторые API отправляют GUID как 22-символьный Base64 вместо 36-символьной hex-строки
string compactGuid = "C0HqetxMckKlZw4CssPUeQ==";
byte[] guidBytes = Convert.FromBase64String(compactGuid);
Guid recovered = new Guid(guidBytes);

Console.WriteLine(recovered);
// 7aea41c0-4cdc-4272-a567-0e02b2c3d479

Base64 в десериализованный JSON через System.Text.Json

C# (.NET 6+)
using System;
using System.Text;
using System.Text.Json;

// Base64-закодированная JSON-полезная нагрузка из очереди сообщений
string encoded = "eyJzZXJ2aWNlIjoicGF5bWVudC1nYXRld2F5IiwicmVnaW9uIjoiZXUtd2VzdC0xIiwicmVwbGljYXMiOjR9";

byte[] jsonBytes = Convert.FromBase64String(encoded);
string json = Encoding.UTF8.GetString(jsonBytes);
Console.WriteLine(json);
// {"service":"payment-gateway","region":"eu-west-1","replicas":4}

// Десериализация в запись
var deployEvent = JsonSerializer.Deserialize<DeployEvent>(jsonBytes);
Console.WriteLine($"{deployEvent!.Service} in {deployEvent.Region}");
// payment-gateway in eu-west-1

record DeployEvent(string Service, string Region, int Replicas);

Base64 в hex-строку

C# (.NET 5+)
using System;

// Хэш SHA-256, хранящийся как Base64
string hashBase64 = "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=";
byte[] hashBytes = Convert.FromBase64String(hashBase64);
string hex = Convert.ToHexString(hashBytes);

Console.WriteLine(hex);
// 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08
Предупреждение:Никогда не десериализуйте ненадёжные данные Base64 с помощью BinaryFormatter. Этот класс имеет известные уязвимости удалённого выполнения кода и устарел в .NET 8+. Если закодированный контент поступает из внешнего источника, разбирайте его как JSON или protobuf вместо использования бинарной сериализации .NET.

Справочник методов декодирования Base64

.NET предоставляет несколько методов декодирования в двух пространствах имён. Класс Convert обрабатывает декодирование общего назначения, тогда как System.Buffers.Text.Base64 ориентирован на высоконагруженные сценарии, где вы уже работаете с сырыми байтовыми буферами UTF-8.

Метод
Возвращает
Тип входных данных
Описание
Convert.FromBase64String(string)
byte[]
string
Декодирует стандартную строку Base64 в массив байт; при невалидных данных выбрасывает FormatException
Convert.TryFromBase64String(string, Span<byte>, out int)
bool
string + Span<byte>
Пытается декодировать в предоставленный вызывающим кодом Span; возвращает false при ошибке вместо исключения (.NET 5+)
Convert.FromBase64CharArray(char[], int, int)
byte[]
char[] + offset + length
Декодирует сегмент массива char; полезно для разбора буферов без создания подстрок
Convert.TryFromBase64Chars(ReadOnlySpan<char>, Span<byte>, out int)
bool
ReadOnlySpan<char> + Span<byte>
Декодирование на основе Span без выделения памяти из char span (.NET 5+)
System.Buffers.Text.Base64.DecodeFromUtf8(ROSpan<byte>, Span<byte>, out int, out int, bool)
OperationStatus
ReadOnlySpan<byte> + Span<byte>
Высокопроизводительное декодирование байт UTF-8; возвращает перечисление OperationStatus (.NET Core 2.1+)
System.Buffers.Text.Base64.DecodeFromUtf8InPlace(Span<byte>, out int)
OperationStatus
Span<byte>
Декодирование на месте с перезаписью входного буфера; нулевое выделение памяти (.NET Core 2.1+)

Convert.TryFromBase64String() — декодирование без исключений

Паттерн Try стандартен в .NET для операций, которые могут завершиться неудачей при пользовательских входных данных. Convert.TryFromBase64String(), доступный начиная с .NET 5, возвращает bool вместо выброса FormatException. Метод записывает декодированные байты в предоставленный вызывающим кодом Span<byte>, что позволяет использовать выделенную на стеке память для небольших нагрузок и полностью избежать работы с кучей.

C# (.NET 5+)
using System;
using System.Text;

string userInput = "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=";

// Выделение на стеке для небольших нагрузок (< 1KB — разумное правило)
Span<byte> buffer = stackalloc byte[256];

if (Convert.TryFromBase64String(userInput, buffer, out int bytesWritten))
{
    string result = Encoding.UTF8.GetString(buffer[..bytesWritten]);
    Console.WriteLine(result);
    // {"host":"10.0.1.50","port":8443}
}
else
{
    Console.WriteLine("Invalid Base64 input — skipping");
}

Этот подход особенно хорош в middleware для обработки запросов или конвейерах валидации, где некорректные входные данные — это норма, а не исключение. Выброс и перехват FormatException при каждом некорректном запросе создаёт ощутимые накладные расходы при высокой нагрузке — TryFromBase64String() полностью устраняет эту проблему.

Валидация входных данных Base64 без декодирования

Распространённый паттерн в API-контроллерах: проверить, является ли входная строка валидным Base64, прежде чем передавать её дальше. Можно использовать TryFromBase64String() в качестве валидатора с временным буфером:

C# (.NET 5+)
using System;

static bool IsValidBase64(string input)
{
    // Вычисляем максимальный размер после декодирования
    Span<byte> buffer = stackalloc byte[((input.Length + 3) / 4) * 3];
    return Convert.TryFromBase64String(input, buffer, out _);
}

// Использование в API-контроллере
Console.WriteLine(IsValidBase64("eyJob3N0IjoiMTAuMC4xLjUwIn0=")); // True
Console.WriteLine(IsValidBase64("not!!valid!!base64"));              // False
Console.WriteLine(IsValidBase64(""));                                // True (пустая строка валидна)
Примечание:Выходной буфер должен быть достаточно большим. Если он слишком мал, TryFromBase64String() вернёт false, даже если входные данные валидны. Для надёжности вычисляйте требуемый размер как (inputLength / 4) * 3.

Декодирование Base64 из файла и ответа API

Чтение Base64-закодированного файла с диска

Сертификаты, зашифрованные блобы и файлы экспорта данных иногда поставляются в виде Base64-текста. Типичный паттерн: прочитать файл как строку, убрать пробелы или переносы строк, которые вызовут FormatException, декодировать в байты и записать бинарный результат. Обратите внимание на обработку ошибок — ошибки файлового ввода-вывода и ошибки формата Base64 следует перехватывать отдельно.

C# (.NET 6+)
using System;
using System.IO;

string inputPath = "tls-cert.pem.b64";
string outputPath = "tls-cert.pem";

try
{
    string encoded = File.ReadAllText(inputPath).Trim();
    // Удаляем переносы строк — декодер .NET их отвергает
    encoded = encoded.Replace("
", "").Replace("
", "");

    byte[] decoded = Convert.FromBase64String(encoded);
    File.WriteAllBytes(outputPath, decoded);

    Console.WriteLine($"Decoded {decoded.Length} bytes -> {outputPath}");
}
catch (IOException ex)
{
    Console.Error.WriteLine($"File error: {ex.Message}");
}
catch (FormatException ex)
{
    Console.Error.WriteLine($"Invalid Base64: {ex.Message}");
}

Декодирование поля Base64 из ответа HTTP API

Облачные API (Azure Key Vault, AWS Secrets Manager, GitHub Contents API) часто возвращают бинарные данные как строки Base64, встроенные в JSON. Рабочий процесс всегда одинаков: выполнить HTTP-запрос, разобрать JSON-ответ, извлечь поле Base64 и декодировать его. В примере ниже используются HttpClient и System.Text.Json — оба встроены в .NET 6+.

C# (.NET 6+)
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer sk-prod-9f8e7d6c");

try
{
    var response = await client.GetAsync("https://api.example.com/secrets/db-password");
    response.EnsureSuccessStatusCode();

    string body = await response.Content.ReadAsStringAsync();

    // API возвращает: {"name":"db-password","value":"cG9zdGdyZXM6eGs5bVAycVI=","version":3}
    using var doc = JsonDocument.Parse(body);
    string encodedValue = doc.RootElement.GetProperty("value").GetString()!;

    byte[] decoded = Convert.FromBase64String(encodedValue);
    string secret = Encoding.UTF8.GetString(decoded);

    Console.WriteLine($"Secret: {secret}");
    // Secret: postgres:xk9mP2qR
}
catch (HttpRequestException ex)
{
    Console.Error.WriteLine($"HTTP error: {ex.Message}");
}
catch (FormatException ex)
{
    Console.Error.WriteLine($"Base64 decode failed: {ex.Message}");
}
Примечание:Разделяйте блоки catch для сетевых ошибок и FormatException. Объединение их затрудняет понимание: API вернул некорректные данные или сам запрос завершился неудачей. В продакшене логируйте сырое значение Base64 (или хотя бы его длину и первые 20 символов) при перехвате FormatException — это значительно упрощает отладку.

Декодирование Base64 из командной строки

Не всегда нужен скомпилированный проект. Инструмент dotnet-script и PowerShell обрабатывают декодирование Base64 в одну строку. Для быстрого просмотра во время отладки это быстрее, чем создавать консольное приложение.

bash
# PowerShell (встроен в Windows, доступен на Linux/macOS)
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("eyJob3N0IjoiMTAuMC4xLjUwIn0="))
# {"host":"10.0.1.50"}

# Нативная команда base64 на Linux / macOS
echo "eyJob3N0IjoiMTAuMC4xLjUwIn0=" | base64 --decode
# {"host":"10.0.1.50"}

# На macOS используется -D вместо --decode
echo "eyJob3N0IjoiMTAuMC4xLjUwIn0=" | base64 -D

# dotnet-script (установка: dotnet tool install -g dotnet-script)
echo 'Console.WriteLine(System.Text.Encoding.UTF8.GetString(Convert.FromBase64String("eyJob3N0IjoiMTAuMC4xLjUwIn0=")));' | dotnet-script eval

# Декодирование и вывод JSON через jq
echo "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=" | base64 --decode | jq .

Для вставки закодированных строк прямо в браузер декодер Base64 от ToolDeck обрабатывает как стандартный, так и URL-безопасный вариант без каких-либо настроек.

Высокопроизводительная альтернатива: System.Buffers.Text.Base64

Класс System.Buffers.Text.Base64, доступный начиная с .NET Core 2.1, работает с сырыми байтовыми спанами UTF-8 вместо строк .NET. Это полностью обходит накладные расходы на преобразование строки в байты — никакого промежуточного выделения строки, никакого шага кодирования UTF-16. Я обращаюсь к нему в middleware ASP.NET Core, где входящие данные уже представлены как ReadOnlySpan<byte> из тела запроса. Создавать string только чтобы передать его в Convert.FromBase64String() — значит удваивать количество выделений памяти без причины. В тестах BenchmarkDotNet на .NET 8 путь на основе спанов примерно в 2-3 раза быстрее для нагрузок до 1 КБ, а разрыв увеличивается с ростом размера входных данных, поскольку давление на GC остаётся постоянным.

C# (.NET 6+)
using System;
using System.Buffers.Text;
using System.Text;

// Имитация сырых байт UTF-8 из тела HTTP-запроса
byte[] utf8Input = Encoding.UTF8.GetBytes("eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=");

byte[] output = new byte[Base64.GetMaxDecodedFromUtf8Length(utf8Input.Length)];

OperationStatus status = Base64.DecodeFromUtf8(
    utf8Input, output, out int bytesConsumed, out int bytesWritten);

if (status == OperationStatus.Done)
{
    string result = Encoding.UTF8.GetString(output.AsSpan(0, bytesWritten));
    Console.WriteLine(result);
    // {"host":"10.0.1.50","port":8443}
    Console.WriteLine($"Consumed: {bytesConsumed}, Written: {bytesWritten}");
    // Consumed: 44, Written: 31
}
else
{
    Console.WriteLine($"Decode failed: {status}");
    // Возможные значения: InvalidData, DestinationTooSmall, NeedMoreData
}

Возвращаемый тип — OperationStatus — перечисление с четырьмя значениями: Done, InvalidData, DestinationTooSmall и NeedMoreData. Последнее значение полезно при частичном декодировании потоковых данных. Для абсолютно нулевого выделения памяти сочетайте этот метод с ArrayPool<byte>.Shared.Rent() вместо new byte[].

DecodeFromUtf8InPlace — перезапись входного буфера

C# (.NET 6+)
using System;
using System.Buffers.Text;
using System.Text;

// Входной буфер перезаписывается декодированными байтами
byte[] data = Encoding.UTF8.GetBytes("c2VydmVyLWNvbmZpZw==");

OperationStatus status = Base64.DecodeFromUtf8InPlace(data, out int bytesWritten);

if (status == OperationStatus.Done)
{
    Console.WriteLine(Encoding.UTF8.GetString(data.AsSpan(0, bytesWritten)));
    // server-config
}
Примечание:DecodeFromUtf8InPlace изменяет входной буфер. Не используйте его, если впоследствии нужна оригинальная строка Base64 — первые bytesWritten байт массива теперь содержат декодированные данные, а остальные являются мусором.

Важно учитывать: System.Buffers.Text.Base64 не обрабатывает URL-безопасный Base64 напрямую. Если входные данные содержат символы - и _ (JWT-токены, например), их всё равно нужно заменить на + и / перед вызовом этих методов. API на основе спанов строго следуют стандартному алфавиту RFC 4648. Варианта DecodeFromUtf8Url не существует — удивительный пробел, учитывая, насколько широко распространён Base64url в современных API.

Вывод в терминал с подсветкой синтаксиса

Библиотека Spectre.Console обеспечивает богатый вывод в терминал, включая подсветку JSON — полезно при создании CLI-инструментов, которые декодируют Base64 и отображают результат. Установите её командой dotnet add package Spectre.Console.

C# (.NET 6+)
using System;
using System.Text;
using Spectre.Console;
using Spectre.Console.Json;

string encoded = "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0MywibWF4Q29ubiI6MTAwfQ==";
byte[] decoded = Convert.FromBase64String(encoded);
string json = Encoding.UTF8.GetString(decoded);

// Форматированный вывод с подсветкой синтаксиса в терминале
AnsiConsole.Write(new JsonText(json));
// Выводит JSON с цветовой подсветкой:
// {
//   "host": "10.0.1.50",
//   "port": 8443,
//   "maxConn": 100
// }

Это особенно удобно для CLI-инструментов, которые получают и декодируют конфигурацию с удалённых сервисов. Декодируйте конфигурацию Base64, десериализуйте в JSON и выведите с подсветкой — всё в несколько строк. Spectre.Console также поддерживает отрисовку таблиц, прогресс-бары и древовидные структуры, если нужно отображать более сложные декодированные структуры данных в терминале.

Предупреждение:Вывод Spectre.Console содержит ANSI escape-последовательности. Не направляйте его в файл и не возвращайте из API — используйте только для отображения в терминале.

Потоковая обработка больших файлов Base64 с CryptoStream

Загрузка файла Base64 размером 500 МБ через File.ReadAllText() с последующим вызовом Convert.FromBase64String() выделяет примерно 700 МБ кучи: сама строка (UTF-16, то есть двойной размер файла) плюс декодированный массив байт. Комбинация CryptoStream + FromBase64Transform декодирует данные по частям, сохраняя постоянное потребление памяти.

C# (.NET 6+)
using System.IO;
using System.Security.Cryptography;

string inputPath = "database-export.sql.b64";
string outputPath = "database-export.sql";

using var inputStream = new FileStream(inputPath, FileMode.Open, FileAccess.Read);
using var transform = new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces);
using var base64Stream = new CryptoStream(inputStream, transform, CryptoStreamMode.Read);
using var outputStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write);

base64Stream.CopyTo(outputStream);

Console.WriteLine($"Decoded {new FileInfo(outputPath).Length} bytes -> {outputPath}");

Флаг FromBase64TransformMode.IgnoreWhiteSpaces обрабатывает Base64 с переносом строк (PEM-файлы, экспорты email) без ручной очистки. Без этого флага переносы строк во входных данных вызовут FormatException. Это ближайший аналог Java-метода getMimeDecoder() в .NET — он молча пропускает пробельные символы при декодировании.

Название CryptoStream вводит в заблуждение — в Base64 нет ничего криптографического. Microsoft поместил FromBase64Transform в пространство имён System.Security.Cryptography, потому что он реализует интерфейс ICryptoTransform, тот же, что используется для AES и других шифровальных преобразований. Сам поток просто передаёт данные через любое преобразование по частям. Думайте о нём как об универсальном конвейере потоковых преобразований, который случайно оказался не в том пространстве имён.

Асинхронная потоковая обработка для сценариев ASP.NET Core

C# (.NET 6+)
using System.IO;
using System.Security.Cryptography;

async Task DecodeStreamAsync(Stream inputStream, string outputPath)
{
    using var transform = new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces);
    using var base64Stream = new CryptoStream(inputStream, transform, CryptoStreamMode.Read);
    await using var outputStream = new FileStream(
        outputPath, FileMode.Create, FileAccess.Write, FileShare.None,
        bufferSize: 81920, useAsync: true);

    await base64Stream.CopyToAsync(outputStream);
}

// Использование в эндпоинте:
// await DecodeStreamAsync(Request.Body, "uploaded-file.bin");
Примечание:В обработчиках ASP.NET Core используйте CopyToAsync вместо CopyTo. Синхронный ввод-вывод в потоке запроса заблокирован по умолчанию в Kestrel и вызывает InvalidOperationException.

Декодирование полезной нагрузки JWT-токена в C#

JWT состоит из трёх закодированных в Base64url сегментов, разделённых точками. Средний сегмент — это полезная нагрузка. Декодировать её можно без подключения JWT-библиотеки — разбить по ., нормализовать символы Base64url, исправить дополнение и вызвать Convert.FromBase64String(). Это почти всегда ставит в тупик с первого раза, потому что JWT использует символы - и _ вместо + и / и убирает дополнение =.

C# (.NET 6+)
using System;
using System.Text;

static string DecodeJwtPayload(string token)
{
    string[] parts = token.Split('.');
    if (parts.Length != 3)
        throw new ArgumentException($"Invalid JWT: expected 3 segments, got {parts.Length}");

    // Берём полезную нагрузку (второй сегмент)
    string payload = parts[1];

    // Заменяем URL-безопасные символы на стандартный Base64
    payload = payload.Replace('-', '+').Replace('_', '/');

    // Дополняем до кратности 4
    switch (payload.Length % 4)
    {
        case 2: payload += "=="; break;
        case 3: payload += "=";  break;
    }

    byte[] bytes = Convert.FromBase64String(payload);
    return Encoding.UTF8.GetString(bytes);
}

// Тест с реальным токеном
string token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"
    + ".eyJzdWIiOiJ1c3ItNjcyIiwiaXNzIjoiYXV0aC5leGFtcGxlLmNvbSIsImV4cCI6MTc0MTk1NjgwMCwicm9sZXMiOlsiYWRtaW4iLCJiaWxsaW5nIl19"
    + ".SIGNATURE_PLACEHOLDER";

Console.WriteLine(DecodeJwtPayload(token));
// {"sub":"usr-672","iss":"auth.example.com","exp":1741956800,"roles":["admin","billing"]}

Важное замечание: этот код только читает полезную нагрузку. Он не проверяет подпись. Для валидации аутентификации в продакшене используйте полноценную библиотеку, например Microsoft.IdentityModel.JsonWebTokens. Но для отладки, логирования и тестовых утверждений этого ручного подхода вполне достаточно.

Разбор декодированной полезной нагрузки JWT в типизированный объект

Получив JSON-строку, десериализуйте её с помощью System.Text.Json для доступа к отдельным клеймам без манипуляций со строками:

C# (.NET 6+)
using System;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

record JwtPayload(
    [property: JsonPropertyName("sub")] string Subject,
    [property: JsonPropertyName("iss")] string Issuer,
    [property: JsonPropertyName("exp")] long Expiration,
    [property: JsonPropertyName("roles")] string[] Roles
);

// После вызова DecodeJwtPayload() из примера выше
string json = DecodeJwtPayload(token);
var claims = JsonSerializer.Deserialize<JwtPayload>(json)!;

Console.WriteLine($"User: {claims.Subject}");
Console.WriteLine($"Issuer: {claims.Issuer}");
Console.WriteLine($"Expires: {DateTimeOffset.FromUnixTimeSeconds(claims.Expiration):u}");
Console.WriteLine($"Roles: {string.Join(", ", claims.Roles)}");
// User: usr-672
// Issuer: auth.example.com
// Expires: 2025-03-14 12:00:00Z
// Roles: admin, billing

Распространённые ошибки

Я сталкивался с каждой из этих ошибок в продакшн-сервисах на C#. Первые две являются причиной большинства багов, связанных с Base64, которые я вижу на код-ревью. Каждая ошибка кажется очевидной в изоляции, но их легко пропустить, когда погружены в более крупную задачу и декодирование Base64 — лишь один шаг в конвейере.

Забыть удалить пробелы из входных данных

Проблема: Строки Base64, прочитанные из конфигурационных файлов, переменных окружения или пользовательского ввода, часто содержат завершающие символы новой строки. Convert.FromBase64String() отвергает любой символ за пределами алфавита Base64, включая \r\n.

Решение: Вызовите .Trim() или .Replace() для удаления пробелов перед декодированием.

Before · C#
After · C#
// Переменная окружения содержит завершающий символ новой строки
string encoded = Environment.GetEnvironmentVariable("DB_PASS_B64")!;
byte[] decoded = Convert.FromBase64String(encoded);
// FormatException: The input is not a valid Base-64 string
string encoded = Environment.GetEnvironmentVariable("DB_PASS_B64")!;
string cleaned = encoded.Replace("\n", "").Replace("\r", "").Trim();
byte[] decoded = Convert.FromBase64String(cleaned);
Console.WriteLine(Encoding.UTF8.GetString(decoded));
Использовать Convert.FromBase64String() для URL-безопасного Base64

Проблема: JWT-токены и некоторые API-данные используют - и _ (URL-безопасный алфавит). Стандартный декодер принимает только + и / — он выбрасывает FormatException при первом символе -.

Решение: Замените - на + и _ на / перед декодированием. Также исправьте дополнение.

Before · C#
After · C#
// Полезная нагрузка JWT — использует URL-безопасный Base64
string payload = "eyJzdWIiOiJ1c3ItNjcyIn0";
byte[] decoded = Convert.FromBase64String(payload);
// FormatException
string payload = "eyJzdWIiOiJ1c3ItNjcyIn0";
payload = payload.Replace('-', '+').Replace('_', '/');
switch (payload.Length % 4)
{
    case 2: payload += "=="; break;
    case 3: payload += "="; break;
}
byte[] decoded = Convert.FromBase64String(payload);
Console.WriteLine(Encoding.UTF8.GetString(decoded));
// {"sub":"usr-672"}
Преобразовывать бинарные данные в строку

Проблема: Вызов Encoding.UTF8.GetString() на декодированных байтах изображения или protobuf-данных даёт мусор. Хуже того, обратное преобразование этой строки в байты молча повреждает данные, так как невалидные последовательности UTF-8 заменяются.

Решение: Храните бинарные данные как byte[] на протяжении всего конвейера. Вызывайте GetString() только когда точно знаете, что контент является текстом.

Before · C#
After · C#
byte[] decoded = Convert.FromBase64String(pngBase64);
string imageStr = Encoding.UTF8.GetString(decoded); // портит бинарные данные
File.WriteAllText("image.png", imageStr); // сломанный файл
byte[] decoded = Convert.FromBase64String(pngBase64);
// Записываем байты напрямую — без преобразования в строку
File.WriteAllBytes("image.png", decoded);
Не указывать кодировку (полагаться на поведение платформы)

Проблема: Некоторые старые API .NET Framework используют текущую кодовую страницу ANSI системы, которая различается между машинами. Сервер Windows с кодовой страницей 1252 и Linux-контейнер с UTF-8 дадут разные строки из одних и тех же байт.

Решение: Всегда явно указывайте Encoding.UTF8. Никогда не полагайтесь на платформенное значение по умолчанию.

Before · C#
After · C#
byte[] decoded = Convert.FromBase64String(encoded);
// Encoding.Default зависит от платформы
string result = Encoding.Default.GetString(decoded);
byte[] decoded = Convert.FromBase64String(encoded);
string result = Encoding.UTF8.GetString(decoded);
// одинаково на Windows, Linux, macOS

Сравнение методов

.NET предлагает больше методов декодирования Base64, чем большинство разработчиков знает. Таблица ниже охватывает все встроенные варианты плюс две наиболее распространённые сторонние альтернативы. Столбец «Выделение памяти» наиболее важен в высоконагруженных сервисах — метод, который выделяет новый byte[] при каждом вызове, создаёт давление на GC в тесных циклах.

Метод
Выделение памяти
Обработка ошибок
Потоковая обработка
Пользовательские типы
Требует установки
Convert.FromBase64String()
Новый byte[]
FormatException
Нет
Нет
Нет (.NET Framework 1.1+)
Convert.TryFromBase64String()
Span от вызывающего
Возвращает false
Нет
Нет
Нет (.NET 5+)
Convert.FromBase64CharArray()
Новый byte[]
FormatException
Нет
Нет
Нет (.NET Framework 1.1+)
Base64.DecodeFromUtf8()
Span от вызывающего
OperationStatus
Частично
Нет
Нет (.NET Core 2.1+)
Base64.DecodeFromUtf8InPlace()
На месте
OperationStatus
Нет
Нет
Нет (.NET Core 2.1+)
CryptoStream + FromBase64Transform
Потоковый буфер
Exception
Да
Нет
Нет (.NET Framework 2.0+)
BouncyCastle Base64
Новый byte[]
Exception
Да
Нет
Да (NuGet)

Для обычных задач: Convert.FromBase64String(). Для горячих путей с валидацией пользовательских данных: TryFromBase64String(). Для middleware ASP.NET Core, работающего с сырыми байтами запроса: Base64.DecodeFromUtf8(). Для больших файлов: CryptoStream + FromBase64Transform. BouncyCastle имеет смысл только если он уже в вашем дереве зависимостей для других криптографических операций.

Для быстрой проверки без компиляции онлайн-декодер Base64 работает быстрее, чем писать консольное приложение.

Часто задаваемые вопросы

Как декодировать строку Base64 в текст в C#?

Вызовите Convert.FromBase64String(), чтобы получить массив байт, затем передайте его в Encoding.UTF8.GetString(). Кодировка должна совпадать с той, что использовалась при кодировании — UTF-8 является безопасным выбором по умолчанию для большинства современных систем. Если входные данные могут содержать пробелы или переносы строк, вызовите .Trim() или удалите их перед декодированием.

C# (.NET 6+)
using System;
using System.Text;

string encoded = "cG9zdGdyZXM6eGs5bVAycVI=";
byte[] bytes = Convert.FromBase64String(encoded);
string result = Encoding.UTF8.GetString(bytes);
Console.WriteLine(result);
// postgres:xk9mP2qR

В чём разница между Convert.FromBase64String() и Convert.TryFromBase64String()?

FromBase64String() выбрасывает FormatException при невалидных входных данных. TryFromBase64String() возвращает bool и записывает результат в предоставленный вызывающим кодом Span<byte>, что делает его подходящим для горячих путей выполнения, где нужно избежать накладных расходов на исключения. TryFromBase64String() требует .NET 5 или более поздней версии.

C# (.NET 6+)
using System;

string input = "maybe-not-valid-base64!!";
Span<byte> buffer = stackalloc byte[256];

if (Convert.TryFromBase64String(input, buffer, out int written))
    Console.WriteLine($"Decoded {written} bytes");
else
    Console.WriteLine("Invalid Base64 input");

Как декодировать полезную нагрузку Base64url JWT в C#?

Разбейте токен по точкам, возьмите второй сегмент, замените - на + и _ на /, дополните до кратности 4 символом =, затем вызовите Convert.FromBase64String(). JWT-токены используют алфавит Base64, безопасный для URL, который стандартный декодер .NET не обрабатывает напрямую.

C# (.NET 6+)
string token = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c3ItNjcyIn0.SIG";
string payload = token.Split('.')[1];
payload = payload.Replace('-', '+').Replace('_', '/');
switch (payload.Length % 4)
{
    case 2: payload += "=="; break;
    case 3: payload += "="; break;
}
byte[] bytes = Convert.FromBase64String(payload);
Console.WriteLine(Encoding.UTF8.GetString(bytes));
// {"sub":"usr-672"}

Как выбрать правильную кодировку при декодировании Base64 в C#?

Используйте Encoding.UTF8 по умолчанию — она обрабатывает ASCII и многобайтовые символы Unicode. Используйте Encoding.ASCII только тогда, когда вы уверены, что данные являются чистым 7-битным ASCII. Используйте Encoding.Unicode (в .NET это UTF-16LE) только если исходные данные были закодированы в UTF-16, что иногда происходит с внутренними строками Windows и экспортами PowerShell.

Почему Convert.FromBase64String() выбрасывает FormatException?

Три распространённые причины: входные данные содержат пробелы или переносы строк (удалите их перед декодированием), входные данные используют URL-безопасные символы - и _ (замените их на + и / соответственно), или дополнение отсутствует или некорректно (общая длина должна быть кратна 4 после добавления дополнения). В отличие от Java, в .NET нет встроенного MIME-декодера, который допускает пробелы — нужно самостоятельно очищать входные данные или использовать CryptoStream с FromBase64Transform в режиме IgnoreWhiteSpaces.

C# (.NET 6+)
// Устранение проблемы с пробелами
string cleaned = rawInput.Replace("\n", "").Replace("\r", "").Trim();
byte[] decoded = Convert.FromBase64String(cleaned);

Можно ли потоково декодировать большие данные Base64 в C#?

Да. Используйте CryptoStream с FromBase64Transform из System.Security.Cryptography. Это декодирует данные по частям при чтении, поэтому потребление памяти остаётся постоянным независимо от размера файла. Передайте FromBase64TransformMode.IgnoreWhiteSpaces, если входные данные содержат переносы строк. В .NET 6+ можно также использовать паттерн IAsyncEnumerable с Base64.DecodeFromUtf8() для ручной пакетной обработки, хотя CryptoStream проще для декодирования файл-в-файл.

C# (.NET 6+)
using System.IO;
using System.Security.Cryptography;

using var input = File.OpenRead("payload.b64");
using var transform = new FromBase64Transform();
using var cryptoStream = new CryptoStream(input, transform, CryptoStreamMode.Read);
using var output = File.Create("payload.bin");
cryptoStream.CopyTo(output);

Связанные инструменты

  • Base64 Encoder — кодирование текста или бинарных данных в Base64 прямо в браузере, удобно для генерации тестовых данных для вставки в юнит-тесты на C#.
  • JWT Decoder — декодирование и просмотр всех трёх сегментов JWT сразу, с инспекцией полезной нагрузки по полям — быстрее, чем писать вспомогательный метод на C#, когда нужно просто прочитать токен.
  • URL Decoder — percent-декодирование URL-закодированных строк, полезно когда ответы API смешивают данные Base64url с percent-закодированными параметрами запроса.
  • JSON Formatter — после декодирования полезной нагрузки JWT в Base64 или конфигурации API вставьте JSON сюда для форматирования и проверки структуры.
Также доступно на:JavaScriptPythonGoJava
AP
Alexei PetrovGame Developer & Unity Engineer

Alexei is a game developer who has shipped multiple titles using Unity and C#. He focuses on gameplay systems, runtime performance, and the serialisation and data-management patterns unique to game development. He writes about Unity scripting, C# async/await in game contexts, asset serialisation, binary data handling, and the intersection of game engineering and general software craftsmanship.

ER
Emma RichardsonТехнический рецензент

Emma is a .NET developer and cloud engineer who builds production APIs and backend services with ASP.NET Core and Azure. She has worked on everything from microservice migrations to real-time SignalR applications. She writes about C# language features, the System.Text.Json and Newtonsoft.Json ecosystems, Azure integrations, and the architectural patterns that make .NET services scalable and maintainable.