Base64 Decode у C# — Посібник Convert.FromBase64String()

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

Використовуйте безкоштовний Base64 Decode Online прямо в браузері — без встановлення.

Спробувати Base64 Decode Online онлайн →

У кожному .NET-проєкті рано чи пізно виникає потреба декодувати Base64 — витягувати рядки підключення з Kubernetes secrets, читати бінарні payload із вебхуків або інспектувати JWT-токени під час налагодження. Для декодування Base64 у C# основним методом є Convert.FromBase64String(), який повертаєbyte[], що потім передається у Encoding.UTF8.GetString() для отримання читабельного тексту. Для швидкої перевірки без написання коду декодер Base64 ToolDeck справляється миттєво у браузері. Цей посібник охоплює Convert.FromBase64String(), Span-based TryFromBase64String() для .NET 5+, високопродуктивний API System.Buffers.Text.Base64, витягування payload 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 secret
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, конфігураційні файли, secrets), закодована як 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

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

  • Encoding.UTF8 — безпечний стандарт. Обробляє ASCII і весь Unicode. Використовуйте його, якщо немає підстав для іншого.
  • Encoding.ASCII — тільки для чистих 7-бітних ASCII-даних. Багатобайтові символи перетворюються на ?.
  • Encoding.Unicode — це UTF-16LE у .NET. Деякі внутрішні рядки 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));  // öüä  (mojibake)

Допоміжна функція декодування з обробкою помилок

Оскільки 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-safe символами, такими як - і _. Хелпер вище автоматично обробляє пробіли.

Декодування 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 payload з черги повідомлень
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}

// Десеріалізація у record
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
Декодує сегмент масиву символів; корисно для парсингу буферів без створення підрядків
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>, що дозволяє використовувати виділену на стеці пам'ять для невеликих payload і повністю обходити купу.

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

string userInput = "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=";

// Виділення на стеці для невеликих payload (< 1 КБ — гарне практичне правило)
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"}

# Linux / macOS — нативна команда base64
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-safe варіанти без жодного налаштування.

Висока продуктивність: System.Buffers.Text.Base64

Клас System.Buffers.Text.Base64, доступний з .NET Core 2.1, працює з сирими UTF-8 байтовими span замість рядків .NET. Це повністю обходить накладні витрати на конвертацію рядок-у-байти — без проміжного виділення рядка, без кроку кодування UTF-16. Я вдаюся до нього у middleware ASP.NET Core, де вхідні дані вже є ReadOnlySpan<byte> з тіла запиту. Створення string лише для передачі у Convert.FromBase64String() подвоює кількість виділень пам'яті без жодної причини. У тестах BenchmarkDotNet на .NET 8 span-based підхід приблизно у 2-3 рази швидший для payload до 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}");
    // Possible: 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-safe Base64 напряму. Якщо вхідні дані використовують символи - і _ (наприклад, JWT-токени), їх все одно потрібно замінити на + і / перед викликом цих методів. Span-based 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

Завантаження 500 МБ Base64-файлу через 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's 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);
}

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

Як декодувати Base64 JWT payload у C#

JWT складається з трьох сегментів, закодованих у Base64url і розділених крапками. Середній сегмент — це payload. Його можна декодувати без підключення 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}");

    // Беремо payload (другий сегмент)
    string payload = parts[1];

    // Замінюємо URL-safe символи на стандартний 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"]}

Важливо: це лише читає payload. Підпис не перевіряється. Для перевірки автентичності у продакшені використовуйте належну бібліотеку, таку як Microsoft.IdentityModel.JsonWebTokens. Але для налагодження, логування та перевірок у тестах цей ручний підхід є всім необхідним.

Парсинг декодованого JWT payload у типізований об'єкт

Отримавши JSON-рядок, десеріалізуйте його за допомогою System.Text.Json для доступу до окремих claims без маніпуляцій з рядками:

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-пов'язаних помилок, які я бачу у code review. Кожна з них виглядає очевидною окремо, але їх легко пропустити, коли ви занурені у більшу задачу, де декодування 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-safe Base64

Проблема: JWT-токени і деякі API-payload використовують - і _ (URL-safe алфавіт). Стандартний декодер приймає лише + і / — він кидає FormatException на першому ж символі -.

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

Before · C#
After · C#
// JWT payload — використовує URL-safe 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);
Невказане кодування (покладання на поведінку платформи)

Проблема: Деякі старіші .NET Framework API за замовчуванням використовують поточну 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);
// consistent across Windows, Linux, macOS

Порівняння методів

.NET пропонує більше методів декодування Base64, ніж більшість розробників усвідомлює. Таблиця нижче охоплює всі вбудовані варіанти плюс дві найпоширеніші сторонні альтернативи. Стовпець "Виділення пам'яті" найважливіший у сервісах із високою пропускною здатністю — метод, що виділяє новий byte[] при кожному виклику, навантажує GC у щільних циклах.

Метод
Виділення пам'яті
Обробка помилок
Стримінг
Кастомні типи
Потребує встановлення
Convert.FromBase64String()
Новий byte[]
FormatException
Ні
Ні
Ні (.NET Framework 1.1+)
Convert.TryFromBase64String()
Caller Span
Повертає false
Ні
Ні
Ні (.NET 5+)
Convert.FromBase64CharArray()
Новий byte[]
FormatException
Ні
Ні
Ні (.NET Framework 1.1+)
Base64.DecodeFromUtf8()
Caller 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-payload JWT у C#?

Розділіть токен за крапками, візьміть другий сегмент, замініть - на + і _ на /, доповніть до кратного 4 символами =, а потім викличте Convert.FromBase64String(). JWT-токени використовують URL-safe алфавіт Base64, який стандартний декодер .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"}

Як вибрати правильне Encoding при декодуванні Base64 у C#?

Використовуйте Encoding.UTF8 за замовчуванням — він обробляє ASCII і багатобайтові Unicode-символи. Використовуйте Encoding.ASCII тільки якщо ви впевнені, що дані є чистим 7-бітним ASCII. Використовуйте Encoding.Unicode (UTF-16LE у .NET) тільки якщо оригінальні дані були закодовані як UTF-16, що іноді трапляється з внутрішніми рядками Windows та експортами PowerShell.

Чому Convert.FromBase64String() кидає FormatException?

Три поширені причини: вхідні дані містять пробіли або переноси рядків (видаліть їх перед декодуванням), вхідні дані використовують URL-safe символи, як - і _ (замініть їх на + і / відповідно), або відступ відсутній чи некоректний (загальна довжина має бути кратна 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 одночасно з поле-за-полем інспекцією payload — швидше, ніж писати хелпер на C#, коли потрібно лише прочитати токен.
  • URL Decoder — percent-декодування URL-encoded рядків; корисно, коли відповіді API поєднують Base64url-дані з percent-encoded параметрами запиту.
  • JSON Formatter — після декодування Base64 JWT payload або конфігурації 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.