Każdy projekt .NET prędzej czy później wymaga dekodowania Base64 — czy to przy pobieraniu connection stringów z sekretów Kubernetes, odczytywaniu binarnych payloadów z webhooków, czy inspekcji tokenów JWT podczas debugowania. Aby zdekodować Base64 w C#, podstawową metodą jest Convert.FromBase64String(), która zwraca byte[], który następnie przekazujemy do Encoding.UTF8.GetString() w celu uzyskania czytelnego tekstu. Do szybkiego sprawdzenia bez pisania kodu, dekoder Base64 ToolDeck obsługuje to natychmiast w przeglądarce. Ten przewodnik omawia Convert.FromBase64String(), oparty na Span TryFromBase64String() dla .NET 5+, wysokowydajne API System.Buffers.Text.Base64, wyodrębnianie payloadów JWT, dekodowanie plików i odpowiedzi API, strumieniowanie z CryptoStream oraz cztery najczęstsze błędy popełniane przez programistów C#.
- ✓Convert.FromBase64String(s) + Encoding.UTF8.GetString(bytes) to standardowy dwuetapowy pipeline — działa we wszystkich wersjach .NET.
- ✓Convert.TryFromBase64String() unika wyjątków przy nieprawidłowym wejściu i zapisuje wynik do Span<byte> — idealne dla ścieżek krytycznych w .NET 5+.
- ✓System.Buffers.Text.Base64.DecodeFromUtf8() zapewnia dekodowanie bez alokacji dla buforów bajtów UTF-8 w serwisach wymagających wysokiej wydajności.
- ✓Tokeny JWT używają Base64url (- i _ zamiast + i /) — musisz znormalizować wejście przed wywołaniem Convert.FromBase64String().
- ✓CryptoStream z FromBase64Transform obsługuje strumieniowe dekodowanie dużych plików bez ładowania wszystkiego do pamięci.
Czym jest dekodowanie Base64?
Kodowanie Base64 konwertuje dane binarne na 64-znakowy alfabet ASCII, dzięki czemu mogą być bezpiecznie przesyłane przez kanały obsługujące wyłącznie tekst — pola JSON, nagłówki HTTP, treści e-maili, atrybuty XML. Każde 3 bajty wejścia zamieniają się w 4 znaki Base64, dlatego dane Base64 są zawsze o około 33% większe od oryginału. Dekodowanie odwraca tę transformację. Dopełnienie = na końcu informuje dekoder, ile bajtów należy odciąć z ostatniej grupy. Pojedynczy = oznacza, że ostatni blok miał 2 bajty; == oznacza 1 bajt. Base64 nie jest szyfrowaniem — każdy może je odwrócić. Celem jest bezpieczny transport przez kanały niszczące surowe dane binarne, nie poufność.
cmVkaXM6Ly9jYWNoZS1wcm9kLmludGVybmFsOjYzNzkvc2Vzc2lvbi1zdG9yZQ==
redis://cache-prod.internal:6379/session-store
Convert.FromBase64String() — standardowa metoda dekodowania
Metoda Convert.FromBase64String() jest w .NET od czasów Framework 1.1. Żadnych pakietów NuGet, żadnych dodatkowych importów poza System — wystarczy ją wywołać i otrzymasz byte[]. Dwuetapowy pipeline dekodowania Base64 do ciągu znaków w C# jest zawsze taki sam: Convert.FromBase64String() do uzyskania bajtów, a następnie Encoding.UTF8.GetString() do zinterpretowania tych bajtów jako tekstu. Haczyk polega na tym, że metoda zwraca surowe bajty, a nie ciąg znaków. Musisz wybrać odpowiednie Encoding do konwersji tych bajtów z powrotem na tekst, a wybór ma większe znaczenie niż większość ludzi się spodziewa. Niezgodne kodowanie w ciszy produkuje mojibake — zniekształcone znaki bez żadnego wyjątku ostrzegającego.
Minimalistyczny przykład działający
using System; using System.Text; // Connection string przechowywany jako Base64 w sekrecie 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
Zawsze używaj Encoding.UTF8, chyba że masz konkretny powód, żeby nie. Środowisko uruchomieniowe .NET reprezentuje ciągi wewnętrznie jako UTF-16, ale większość danych przekraczających granice systemów (odpowiedzi API, pliki konfiguracyjne, sekrety) jest zakodowana w UTF-8. Użycie Encoding.ASCII na danych zawierających znaki wielobajtowe w ciszy zastąpi je znakiem ? — bez wyjątku, tylko uszkodzone wyjście.
Weryfikacja round-trip
using System; using System.Text; string original = "postgres://db-admin:Kx8!mQ@db-prod.us-east-1.internal:5432/orders"; // Kodowanie string encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(original)); Console.WriteLine(encoded); // cG9zdGdyZXM6Ly9kYi1hZG1pbjpLeDghbVFAZGItcHJvZC51cy1lYXN0LTEuaW50ZXJuYWw6NTQzMi9vcmRlcnM= // Dekodowanie byte[] decoded = Convert.FromBase64String(encoded); string recovered = Encoding.UTF8.GetString(decoded); Console.WriteLine(recovered == original); // True
Wybór właściwego kodowania
Encoding przekazane do GetString() musi odpowiadać temu, które było użyte podczas oryginalnego kodowania danych. Wybierz złe i otrzymasz śmieciowe znaki bez żadnego wyjątku — dekoder chętnie produkuje nonsens. Praktyczne zestawienie:
Encoding.UTF8— bezpieczny domyślny. Obsługuje ASCII i cały Unicode. Używaj tego, chyba że wiesz inaczej.Encoding.ASCII— tylko dla czystych 7-bitowych danych ASCII. Znaki wielobajtowe zamieniają się na?.Encoding.Unicode— to jest UTF-16LE w .NET. Niektóre wewnętrzne ciągi Windows i eksporty PowerShell używają tego.Encoding.Latin1— starsze zachodnioeuropejskie kodowanie. Pojawia się w starych serwisach SOAP i integracjach mainframe.
using System;
using System.Text;
// Te same bajty, różne kodowania — różne wyniki
byte[] decoded = Convert.FromBase64String("w7bDvMOk");
Console.WriteLine(Encoding.UTF8.GetString(decoded)); // oua (poprawnie)
Console.WriteLine(Encoding.ASCII.GetString(decoded)); // ?????? (utracone)
Console.WriteLine(Encoding.Latin1.GetString(decoded)); // öüä (mojibake)Pomocnik do dekodowania z obsługą błędów do ponownego użycia
Ponieważ Convert.FromBase64String() rzuca wyjątek przy złym wejściu, a obsługa białych znaków pojawia się ciągle, w większości projektów trzymam mały helper:
using System;
using System.Text;
static class Base64Helper
{
public static string? DecodeToString(string encoded)
{
if (string.IsNullOrWhiteSpace(encoded))
return null;
// Usuń białe znaki, które dekoder .NET odrzuca
string cleaned = encoded
.Replace("
", "")
.Replace("
", "")
.Replace(" ", "")
.Trim();
try
{
byte[] bytes = Convert.FromBase64String(cleaned);
return Encoding.UTF8.GetString(bytes);
}
catch (FormatException)
{
return null; // lub rzuć wyjątek specyficzny dla domeny
}
}
}
// Użycie
string? decoded = Base64Helper.DecodeToString(" cmVkaXM6Ly9jYWNoZQ== \n");
Console.WriteLine(decoded); // redis://cacheConvert.FromBase64String() rzuca FormatException, jeśli wejście zawiera znaki spoza alfabetu Base64 — w tym spacje, znaki nowej linii i znaki bezpieczne dla URL, takie jak - i _. Powyższy helper obsługuje białe znaki automatycznie.Dekodowanie Base64 do niestandardowych typów
Dekoder zawsze zwraca byte[]. To, co zrobisz z tymi bajtami, zależy od oryginalnych danych. Czasem to GUID przechowywany jako 16 surowych bajtów, czasem zserializowana wiadomość protobuf, a czasem znacznik czasu w formacie binarnym. Oto konwersje, po które sięgam najczęściej.
Base64 do GUID
using System; // Niektóre API przesyłają GUIDy jako 22-znakowy Base64 zamiast 36-znakowego ciągu hex string compactGuid = "C0HqetxMckKlZw4CssPUeQ=="; byte[] guidBytes = Convert.FromBase64String(compactGuid); Guid recovered = new Guid(guidBytes); Console.WriteLine(recovered); // 7aea41c0-4cdc-4272-a567-0e02b2c3d479
Base64 do zdeserializowanego JSON z System.Text.Json
using System;
using System.Text;
using System.Text.Json;
// Payload JSON zakodowany Base64 z kolejki wiadomości
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}
// Deserializacja do rekordu
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 do ciągu hex
using System; // Hash SHA-256 przechowywany jako Base64 string hashBase64 = "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="; byte[] hashBytes = Convert.FromBase64String(hashBase64); string hex = Convert.ToHexString(hashBytes); Console.WriteLine(hex); // 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08
BinaryFormatter. Ma on znane luki umożliwiające zdalne wykonanie kodu i jest przestarzały w .NET 8+. Jeśli zakodowana treść pochodzi z zewnętrznego źródła, parsuj ją jako JSON lub protobuf zamiast używać binarnej serializacji .NET.Tabela metod dekodowania Base64
.NET udostępnia wiele metod dekodowania w dwóch przestrzeniach nazw. Klasa Convert obsługuje dekodowanie ogólnego przeznaczenia, podczas gdy System.Buffers.Text.Base64 jest przeznaczony dla scenariuszy wysokoprzepustowych, gdzie pracujesz już z surowymi buforami bajtów UTF-8.
Convert.TryFromBase64String() — dekodowanie bez wyjątków
Wzorzec Try jest standardem w .NET dla operacji, które mogą się nie powieść przy danych wejściowych użytkownika. Convert.TryFromBase64String(), dostępne od .NET 5, zwraca bool zamiast rzucać FormatException. Zapisuje zdekodowane bajty do dostarczonego przez wywołującego Span<byte>, co oznacza, że można używać pamięci alokowanej na stosie dla małych payloadów i całkowicie pominąć stertę.
using System;
using System.Text;
string userInput = "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=";
// Alokacja na stosie dla małych payloadów (< 1KB to bezpieczna reguła)
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");
}To podejście sprawdza się w middleware żądań lub potokach walidacji, gdzie nieprawidłowe wejście jest oczekiwane, nie wyjątkowe. Rzucanie i łapanie FormatException przy każdym zniekształconym żądaniu dodaje mierzalne obciążenie przy dużej skali — TryFromBase64String() całkowicie tego unika.
Walidacja wejścia Base64 bez dekodowania
Częsty wzorzec w kontrolerach API: sprawdź, czy wejście jest prawidłowym Base64 przed przekazaniem go dalej. Możesz użyć TryFromBase64String() jako walidatora, alokując jednorazowy bufor:
using System;
static bool IsValidBase64(string input)
{
// Oblicz maksymalny rozmiar po dekodowaniu
Span<byte> buffer = stackalloc byte[((input.Length + 3) / 4) * 3];
return Convert.TryFromBase64String(input, buffer, out _);
}
// Użycie w kontrolerze API
Console.WriteLine(IsValidBase64("eyJob3N0IjoiMTAuMC4xLjUwIn0=")); // True
Console.WriteLine(IsValidBase64("not!!valid!!base64")); // False
Console.WriteLine(IsValidBase64("")); // True (puste jest prawidłowe)TryFromBase64String() zwraca false nawet gdy wejście jest prawidłowe. Oblicz wymagany rozmiar jako (inputLength / 4) * 3 dla pewności.Dekodowanie Base64 z pliku i odpowiedzi API
Odczyt pliku zakodowanego Base64 z dysku
Certyfikaty, zaszyfrowane bloby i pliki eksportu danych są czasem dostarczane jako tekst Base64. Typowy wzorzec: odczytaj plik jako ciąg znaków, usuń białe znaki lub znaki nowej linii powodujące FormatException, zdekoduj do bajtów i zapisz binarne wyjście. Zwróć uwagę na obsługę błędów — błędy I/O pliku i błędy formatu Base64 powinny być przechwytywane osobno.
using System;
using System.IO;
string inputPath = "tls-cert.pem.b64";
string outputPath = "tls-cert.pem";
try
{
string encoded = File.ReadAllText(inputPath).Trim();
// Usuń znaki nowej linii — dekoder .NET je odrzuca
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}");
}Dekodowanie pola Base64 z odpowiedzi HTTP API
Chmurowe API (Azure Key Vault, AWS Secrets Manager, GitHub Contents API) często zwracają dane binarne jako ciągi Base64 osadzone w JSON. Workflow jest zawsze taki sam: wykonaj żądanie HTTP, sparsuj odpowiedź JSON, wyodrębnij pole Base64 i zdekoduj je. Poniższy przykład używa HttpClient i System.Text.Json — obu wbudowanych w .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 zwraca: {"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 dla błędów sieciowych i FormatException. Łączenie ich razem utrudnia ustalenie, czy API zwróciło błędne dane, czy samo żądanie się nie powiodło. W produkcji loguj surową wartość Base64 (lub przynajmniej jej długość i pierwsze 20 znaków) gdy łapiesz FormatException — znacznie ułatwia to debugowanie.Dekodowanie Base64 z wiersza poleceń
Nie zawsze potrzebujesz skompilowanego projektu. Narzędzie dotnet-script i PowerShell obsługują dekodowanie Base64 jednolinijkowymi poleceniami. Do szybkiej inspekcji podczas debugowania są szybsze niż tworzenie aplikacji konsolowej.
# PowerShell (wbudowany w Windows, dostępny na Linux/macOS)
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("eyJob3N0IjoiMTAuMC4xLjUwIn0="))
# {"host":"10.0.1.50"}
# Natywna komenda base64 na Linux / macOS
echo "eyJob3N0IjoiMTAuMC4xLjUwIn0=" | base64 --decode
# {"host":"10.0.1.50"}
# macOS używa -D zamiast --decode
echo "eyJob3N0IjoiMTAuMC4xLjUwIn0=" | base64 -D
# dotnet-script (instalacja: dotnet tool install -g dotnet-script)
echo 'Console.WriteLine(System.Text.Encoding.UTF8.GetString(Convert.FromBase64String("eyJob3N0IjoiMTAuMC4xLjUwIn0=")));' | dotnet-script eval
# Dekoduj i sformatuj JSON z jq
echo "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=" | base64 --decode | jq .Do wklejania zakodowanych ciągów bezpośrednio w przeglądarce, dekoder Base64 ToolDeck obsługuje zarówno standardowe, jak i bezpieczne dla URL warianty bez żadnej konfiguracji.
Wysokowydajna alternatywa: System.Buffers.Text.Base64
Klasa System.Buffers.Text.Base64, dostępna od .NET Core 2.1, operuje na surowych spanach bajtów UTF-8 zamiast na ciągach .NET. Całkowicie omija narzut konwersji ciąg-bajty — żadnej pośredniej alokacji ciągu, żadnego kroku kodowania UTF-16. Sięgam po nią w middleware ASP.NET Core, gdzie przychodzące dane są już ReadOnlySpan<byte> z treści żądania. Tworzenie string tylko po to, żeby przekazać go do Convert.FromBase64String() podwaja alokacje bez żadnego powodu. W testach BenchmarkDotNet na .NET 8, ścieżka oparta na Span jest mniej więcej 2-3x szybsza dla payloadów poniżej 1 KB, a różnica rośnie dla większych danych, ponieważ presja na GC pozostaje stała.
using System;
using System.Buffers.Text;
using System.Text;
// Symulacja surowych bajtów UTF-8 z treści żądania 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}");
// Możliwe: InvalidData, DestinationTooSmall, NeedMoreData
}Typem zwracanym jest OperationStatus — enum z czterema wartościami: Done, InvalidData, DestinationTooSmall i NeedMoreData. Ostatnia jest przydatna do częściowego dekodowania strumieniowanych danych. Dla absolutnie zerowej alokacji, połącz to z ArrayPool<byte>.Shared.Rent() zamiast new byte[].
DecodeFromUtf8InPlace — nadpisanie bufora wejściowego
using System;
using System.Buffers.Text;
using System.Text;
// Bufor wejściowy zostaje nadpisany zdekodowanymi bajtami
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 modyfikuje bufor wejściowy. Nie używaj tej metody, jeśli potrzebujesz oryginalnego ciągu Base64 później — pierwsze bytesWritten bajtów tablicy zawiera teraz zdekodowane dane, a reszta to śmieciowe dane.Warto wiedzieć: System.Buffers.Text.Base64 nie obsługuje bezpośrednio Base64 bezpiecznego dla URL. Jeśli wejście używa znaków - i _ (np. tokeny JWT), nadal musisz zamienić je na + i / przed wywołaniem tych metod. API oparte na Span ściśle stosują standardowy alfabet RFC 4648. Nie ma wariantu DecodeFromUtf8Url — zaskakująca luka biorąc pod uwagę, jak powszechny jest Base64url w nowoczesnych API.
Wyjście terminala z podświetlaniem składni
Biblioteka Spectre.Console daje bogate wyjście terminala, w tym podświetlanie JSON — przydatne przy budowaniu narzędzi CLI, które dekodują Base64 i wyświetlają wynik. Zainstaluj ją poleceniem dotnet add package Spectre.Console.
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);
// Ładne wyświetlenie z podświetlaniem składni w terminalu
AnsiConsole.Write(new JsonText(json));
// Wyświetla kolorowy JSON:
// {
// "host": "10.0.1.50",
// "port": 8443,
// "maxConn": 100
// }Jest to szczególnie przydatne dla narzędzi CLI, które pobierają i dekodują konfigurację z zewnętrznych serwisów. Zdekoduj konfigurację Base64, deserializuj do JSON i wydrukuj podświetlone wyjście — wszystko w kilku linijkach. Spectre.Console ma również renderowanie tabel, paski postępu i widoki drzewa, jeśli potrzebujesz wyświetlić bardziej złożone zdekodowane struktury danych w terminalu.
Strumieniowanie dużych plików Base64 z CryptoStream
Wczytanie pliku Base64 o rozmiarze 500 MB za pomocą File.ReadAllText() i wywołanie Convert.FromBase64String() alokuje około 700 MB sterty: sam ciąg (UTF-16, więc dwukrotność rozmiaru pliku) plus zdekodowana tablica bajtów. Kombinacja CryptoStream + FromBase64Transform dekoduje we fragmentach, utrzymując stałe zużycie pamięci.
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}");Flaga FromBase64TransformMode.IgnoreWhiteSpaces obsługuje Base64 z zawijaniem wierszy (pliki PEM, eksporty e-mail) bez ręcznego czyszczenia. Bez tej flagi znaki nowej linii w wejściu powodują FormatException. To najbliższy odpowiednik Javy getMimeDecoder() w .NET — cicho pomija białe znaki podczas dekodowania.
Nazwa CryptoStream jest myląca — w Base64 nie ma nic kryptograficznego. Microsoft umieścił FromBase64Transform w przestrzeni nazw System.Security.Cryptography, ponieważ implementuje ICryptoTransform, ten sam interfejs używany dla AES i innych szyfrów. Sam strumień po prostu przeprowadza dane przez dowolną transformację we fragmentach. Myśl o tym jako o ogólnym potoku transformacji strumieniowej, który przypadkowo znajduje się w złej przestrzeni nazw.
Asynchroniczne strumieniowanie dla scenariuszy ASP.NET Core
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);
}
// Użycie w endpoincie:
// await DecodeStreamAsync(Request.Body, "uploaded-file.bin");CopyTo na CopyToAsync w handlerach ASP.NET Core. Synchroniczne I/O na wątku żądania jest domyślnie blokowane przez Kestrel i rzuca InvalidOperationException.Jak zdekodować payload tokena JWT zakodowany Base64 w C#
JWT ma trzy segmenty zakodowane w Base64url oddzielone kropkami. Środkowy segment to payload. Możesz go zdekodować bez dołączania biblioteki JWT — podziel po ., znormalizuj znaki Base64url, napraw dopełnienie i wywołaj Convert.FromBase64String(). Prawie każdy napotyka tu problem za pierwszym razem, bo JWT używa - i _ zamiast + i /, i pomija dopełnienie =.
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}");
// Weź payload (drugi segment)
string payload = parts[1];
// Zamień znaki bezpieczne dla URL na standardowy Base64
payload = payload.Replace('-', '+').Replace('_', '/');
// Uzupełnij do wielokrotności 4
switch (payload.Length % 4)
{
case 2: payload += "=="; break;
case 3: payload += "="; break;
}
byte[] bytes = Convert.FromBase64String(payload);
return Encoding.UTF8.GetString(bytes);
}
// Test z tokenem o prawdziwej strukturze
string token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"
+ ".eyJzdWIiOiJ1c3ItNjcyIiwiaXNzIjoiYXV0aC5leGFtcGxlLmNvbSIsImV4cCI6MTc0MTk1NjgwMCwicm9sZXMiOlsiYWRtaW4iLCJiaWxsaW5nIl19"
+ ".SIGNATURE_PLACEHOLDER";
Console.WriteLine(DecodeJwtPayload(token));
// {"sub":"usr-672","iss":"auth.example.com","exp":1741956800,"roles":["admin","billing"]}Krótka uwaga: to tylko odczytuje payload. Nie weryfikuje podpisu. Do walidacji uwierzytelniania w produkcji, użyj odpowiedniej biblioteki, takiej jak Microsoft.IdentityModel.JsonWebTokens. Ale do debugowania, logowania i asercji w testach, to ręczne podejście jest wszystkim, czego potrzebujesz.
Parsowanie zdekodowanego payloadu JWT do typowanego obiektu
Gdy masz już ciąg JSON, deserializuj go za pomocą System.Text.Json aby uzyskać dostęp do poszczególnych claims bez manipulacji ciągami:
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
);
// Po wywołaniu DecodeJwtPayload() powyżej
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, billingCzęste błędy
Napotkałem każdy z tych błędów w produkcyjnych serwisach C#. Pierwsze dwa są odpowiedzialne za większość błędów związanych z Base64, które widzę podczas code review. Każdy błąd wydaje się oczywisty w izolacji, ale łatwo go przeoczyć, gdy jesteś zagłębiony w większą funkcjonalność, a dekodowanie Base64 jest tylko jednym krokiem w pipeline.
Problem: Ciągi Base64 odczytane z plików konfiguracyjnych, zmiennych środowiskowych lub danych wprowadzanych przez użytkownika często mają końcowe znaki nowej linii. Convert.FromBase64String() odrzuca dowolny znak spoza alfabetu Base64, w tym \r\n.
Rozwiązanie: Wywołaj .Trim() lub .Replace(), aby usunąć białe znaki przed dekodowaniem.
// Zmienna środowiskowa ma końcowy znak nowej linii
string encoded = Environment.GetEnvironmentVariable("DB_PASS_B64")!;
byte[] decoded = Convert.FromBase64String(encoded);
// FormatException: The input is not a valid Base-64 stringstring 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));Problem: Tokeny JWT i niektóre payloady API używają - i _ (alfabet bezpieczny dla URL). Standardowy dekoder akceptuje tylko + i / — rzuca FormatException na pierwszym znaku -.
Rozwiązanie: Zamień - na + i _ na / przed dekodowaniem. Napraw też dopełnienie.
// Payload JWT — używa Base64 bezpiecznego dla URL 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"}Problem: Wywołanie Encoding.UTF8.GetString() na zdekodowanych bajtach obrazu lub protobuf produkuje śmieci. Co gorsza, konwersja tego ciągu z powrotem na bajty cicho uszkadza dane, bo nieprawidłowe sekwencje UTF-8 są zastępowane.
Rozwiązanie: Trzymaj dane binarne jako byte[] przez cały pipeline. Wywołuj GetString() tylko gdy wiesz, że treść jest tekstem.
byte[] decoded = Convert.FromBase64String(pngBase64);
string imageStr = Encoding.UTF8.GetString(decoded); // uszkadza dane binarne
File.WriteAllText("image.png", imageStr); // uszkodzony plikbyte[] decoded = Convert.FromBase64String(pngBase64);
// Zapisz bajty bezpośrednio — bez konwersji na ciąg
File.WriteAllBytes("image.png", decoded);Problem: Niektóre starsze API .NET Framework domyślnie używają bieżącej strony kodowej ANSI systemu, która różni się między maszynami. Serwer Windows ze stroną kodową 1252 i kontener Linux z UTF-8 produkują różne ciągi z tych samych bajtów.
Rozwiązanie: Zawsze jawnie podawaj Encoding.UTF8. Nigdy nie polegaj na domyślnym ustawieniu platformy.
byte[] decoded = Convert.FromBase64String(encoded); // Encoding.Default różni się między platformami string result = Encoding.Default.GetString(decoded);
byte[] decoded = Convert.FromBase64String(encoded); string result = Encoding.UTF8.GetString(decoded); // spójne na Windows, Linux, macOS
Porównanie metod
.NET oferuje więcej metod dekodowania Base64, niż większość programistów zdaje sobie sprawę. Poniższa tabela obejmuje każdą wbudowaną opcję plus dwie najpopularniejsze alternatywy zewnętrzne. Kolumna „Alokacja" ma największe znaczenie w serwisach o wysokiej przepustowości — metoda alokująca nowy byte[] przy każdym wywołaniu wywiera presję na GC w ciasnych pętlach.
Do codziennej pracy: Convert.FromBase64String(). Dla ścieżek krytycznych, gdzie walidujemy wejście użytkownika: TryFromBase64String(). Dla middleware ASP.NET Core operującego na surowych bajtach żądania: Base64.DecodeFromUtf8(). Dla dużych plików: CryptoStream + FromBase64Transform. BouncyCastle ma sens tylko wtedy, gdy jest już w drzewie zależności do innych operacji kryptograficznych.
Do szybkiej weryfikacji bez kompilowania czegokolwiek, dekoder Base64 online jest szybszy niż pisanie jednorazowej aplikacji konsolowej.
Często zadawane pytania
Jak zdekodować ciąg Base64 do tekstu w C#?
Wywołaj Convert.FromBase64String(), aby uzyskać tablicę bajtów, a następnie przekaż ją do Encoding.UTF8.GetString(). Kodowanie musi odpowiadać temu, które było użyte podczas kodowania — UTF-8 jest bezpiecznym domyślnym wyborem dla niemal wszystkich nowoczesnych systemów. Jeśli wejście może zawierać białe znaki lub znaki nowej linii, wywołaj .Trim() lub usuń je przed dekodowaniem.
using System; using System.Text; string encoded = "cG9zdGdyZXM6eGs5bVAycVI="; byte[] bytes = Convert.FromBase64String(encoded); string result = Encoding.UTF8.GetString(bytes); Console.WriteLine(result); // postgres:xk9mP2qR
Jaka jest różnica między Convert.FromBase64String() a Convert.TryFromBase64String()?
FromBase64String() rzuca FormatException przy nieprawidłowym wejściu. TryFromBase64String() zwraca bool i zapisuje wynik do dostarczonego przez wywołującego Span<byte>, co sprawia, że jest odpowiednie dla ścieżek krytycznych wydajnościowo, gdzie chcemy uniknąć narzutu wyjątków. TryFromBase64String() wymaga .NET 5 lub nowszego.
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");Jak zdekodować payload JWT zakodowany Base64url w C#?
Podziel token na segmenty po kropkach, weź drugi segment, zamień - na + i _ na /, uzupełnij do wielokrotności 4 znakiem =, a następnie wywołaj Convert.FromBase64String(). Tokeny JWT używają alfabetu Base64 bezpiecznego dla URL, którego standardowy dekoder .NET nie obsługuje bezpośrednio.
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"}Jak wybrać odpowiednie kodowanie przy dekodowaniu Base64 w C#?
Używaj Encoding.UTF8 jako domyślnego — obsługuje ASCII i wielobajtowe znaki Unicode. Stosuj Encoding.ASCII tylko wtedy, gdy masz pewność, że dane są czystym 7-bitowym ASCII. Używaj Encoding.Unicode (czyli UTF-16LE w .NET) wyłącznie wtedy, gdy oryginalne dane były zakodowane jako UTF-16, co zdarza się przy wewnętrznych ciągach Windows i eksportach PowerShell.
Dlaczego Convert.FromBase64String() rzuca FormatException?
Trzy częste przyczyny: wejście zawiera białe znaki lub znaki nowej linii (usuń je przed dekodowaniem), wejście używa znaków bezpiecznych dla URL, takich jak - i _ (zamień je odpowiednio na + i /), albo brakuje dopełnienia lub jest nieprawidłowe (łączna długość po dopełnieniu musi być wielokrotnością 4). W odróżnieniu od Javy, .NET nie ma wbudowanego dekodera MIME tolerującego białe znaki — musisz samodzielnie oczyścić wejście lub użyć CryptoStream z FromBase64Transform w trybie IgnoreWhiteSpaces.
// Usunięcie białych znaków
string cleaned = rawInput.Replace("\n", "").Replace("\r", "").Trim();
byte[] decoded = Convert.FromBase64String(cleaned);Czy mogę strumieniować dekodowanie dużych danych Base64 w C#?
Tak. Użyj CryptoStream z FromBase64Transform z przestrzeni nazw System.Security.Cryptography. Dekoduje on dane we fragmentach podczas odczytu, dzięki czemu użycie pamięci pozostaje stałe niezależnie od rozmiaru pliku. Przekaż FromBase64TransformMode.IgnoreWhiteSpaces, jeśli wejście zawiera znaki nowej linii. W .NET 6+ możesz też użyć wzorca IAsyncEnumerable z Base64.DecodeFromUtf8() do ręcznego przetwarzania fragmentowanego, choć CryptoStream jest prostszym rozwiązaniem przy dekodowaniu plik-do-pliku.
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);Powiązane narzędzia
- Base64 Encoder — koduj tekst lub dane binarne do Base64 w przeglądarce, przydatne do generowania fixtures testowych do wklejenia w testy jednostkowe C#.
- JWT Decoder — dekoduj i analizuj wszystkie trzy segmenty JWT naraz, z inspekcją payloadu pole po polu — szybsze niż pisanie helpera C#, gdy chcesz tylko odczytać token.
- URL Decoder — dekoduj ciągi zakodowane percent-encoding, przydatne gdy odpowiedzi API mieszają dane Base64url z parametrami zapytania zakodowanymi procentowo.
- JSON Formatter — po zdekodowaniu payloadu JWT lub konfiguracji API zakodowanej Base64, wklej JSON tutaj, żeby sformatować i zwalidować strukturę.