Base64-Dekodierung in C# (CSharp): Ein vollständiger Leitfaden
Sie erkennen es in einem Augenblick: ein Strom aus Buchstaben und Ziffern, hin und wieder ein + oder /, und vielleicht ein = oder zwei, die am Ende herumbaumeln. Irgendwo zwischen einer API-Antwort, einem E-Mail-Anhang, einer Config-Datei und einem JWT hat jemand binäre Daten in Text gepackt, und jetzt ist es Ihre Aufgabe, es wieder aufzumachen. Das hier ist die Dekodier-Seite von Base64 in C#, und die erste gute Nachricht ist, dass Sie nichts außer dem Framework brauchen. Der Dekodierer lebt seit mehr als zwanzig Jahren im System-Namespace, und jede moderne .NET-Runtime liefert ihn immer noch mit, mit mehr Optionen und besserer Performance als das Original.
Eine kurze Auffrischung, denn die Startseite dieser Site erklärt das Format vollständig: Vier Zeichen aus einem Alphabet mit 64 Symbolen tragen drei Bytes Daten, und ein oder zwei =-Zeichen am Ende markieren die übrig gebliebenen Bytes. Das Dekodieren führt diesen Tausch in umgekehrter Richtung aus, also ist das Ergebnis ungefähr drei Viertel so groß wie die Eingabe. Mit der Form des Problems im Kopf öffnen wir ein paar Pakete.
Die Dekodierer-Familie: Kennen Sie Ihre Optionen
Bevor das erste Beispiel kommt, hier die ganze Familie an Dekodier-APIs, zu der Sie greifen können, und die Situation, für die jede einzelne gebaut wurde. Alles, was hier aufgeführt ist, ist Teil der .NET-Runtime selbst, ausgenommen die URL-sichere Klasse auf älteren Frameworks, die auf einem kleinen NuGet-Paket mitreitet:
| API | Verfügbar seit | Wofür es gut ist |
|---|---|---|
Convert.FromBase64String(string) |
.NET Framework 1.1 (2003) | Der Klassiker. Ein String hinein, ein frisches byte[] hinaus. Überspringt normalen Weißraum, wirft bei allem anderen eine Exception. |
Convert.FromBase64CharArray(char[], int, int) |
.NET Framework 1.1 (2003) | Dasselbe Dekodieren, liest aber aus einem Ausschnitt eines Zeichenpuffers, den Sie bereits besitzen. |
Convert.TryFromBase64String, Convert.TryFromBase64Chars |
.NET Core 2.1 (2018) | Boolesch statt Exceptionen, schreibt in einen Span, den Sie bereitstellen. Die freundliche Wache für nicht vertrauenswürdige Eingaben. |
System.Buffers.Text.Base64 |
.NET Core 2.1 (2018) | Die strenge Span-API: Statuscodes statt Exceptionen, In-Place-Dekodieren und IsValid-Vorabprüfungen. |
System.Buffers.Text.Base64Url |
.NET 9 (2024) | Das URL-sichere Alphabet (- und _ statt + und /), mit oder ohne Padding. Auf .NET Framework 4.6.2+ und .NET Standard 2.0: das Microsoft.Bcl.Memory-NuGet-Paket. |
FromBase64Transform + CryptoStream |
.NET Framework 1.1 (2003) | Streaming-Dekodieren: Datei zu Datei, Netzwerk zu Festplatte, Chunk für Chunk, ohne den ganzen Payload zu laden. |
Wenn Ihr Projekt eine .NET-Version ab 2018 als Ziel hat, sind die ersten vier Zeilen an Bord. Base64Url braucht .NET 9 oder neuer, oder auf allem Älteren das Microsoft.Bcl.Memory-Paket. Und ein Ausblick: Die .NET-11-Bibliotheken, zur Zeit des Schreibens in der Vorschau, mit einer allgemeinen Veröffentlichung für Ende 2026 erwartet, fügen den vorhandenen Typen weitere Base64-Bequemlichkeits-APIs und Overloads hinzu, also wächst die Familie weiter. Nichts anderes in diesem Artikel braucht ein Paket.
Das Arbeitstier: Convert.FromBase64String
Neunzig Prozent des Dekodier-Alltags in C# sind ein einziger Aufruf. Geben Sie ihm einen String, und er gibt Ihnen genau die Bytes zurück, die darin gepackt waren:
using System;
using System.Text;
string packed = "TWFu";
byte[] bytes = Convert.FromBase64String(packed);
string text = Encoding.UTF8.GetString(bytes);
Console.WriteLine(text);
// Man
Drei Details lohnen es, ins Gedächtnis einzuprägen. Erstens ist der Rückgabewert Bytes, nicht Text: Es ist ein byte[], der Dekodierer ist von Anfang bis Ende byte-orientiert, und genau das wollen Sie, denn der Payload könnte ein Satz sein, ein PNG, ein Zertifikat oder ein Hash, und keines davon sollte eine Sonderbehandlung bekommen. Der Sprung von Bytes zurück zu lesbarem Text ist ein separater, bewusster Schritt über Encoding, und genau in diesem Schritt leben die Zeichensatz-Entscheidungen (mehr dazu unten). Zweitens alloziert der Dekodierer bei jedem Aufruf ein frisches Array, bemessen auf die dekodierte Länge, also gibt er Ihnen nie einen Puffer mit Reserven. Drittens ist der Vertrag klein und ehrlich: Ein leerer String dekodiert zu einem leeren Array, eine null-Referenz wirft ArgumentNullException, und alles, was kein gültiges Base64 ist, wirft FormatException. Alles Weitere ist eine Ausarbeitung dieser drei Regeln.
Was es verzeiht und was es ablehnt
Hier hat der C#-Dekodierer eine Persönlichkeit, und eine charakterstarke dazu. Bei genau einer Sache ist er großzügig - beim Weißraum - und bei allem anderen unbarmherzig. Der Dekodierer überspringt exakt vier Zeichen, wo immer sie im String auftauchen: das Leerzeichen (U+0020), der Tab (U+0009), der Zeilenumbruch (U+000A) und der Wagenrücksetzer (U+000D). Diese Politik ist eine bewusste Verbeugung vor der E-Mail, wo Base64-Payloads in 76-Zeichen-Zeilen verpackt ankommen, und es bedeutet, dass ein MIME-umwickelter Anhang mit null Vorverarbeitung dekodiert wird. Alles außerhalb des Alphabets mit 64 Symbolen, alles, was die Längenregeln bricht, oder alles mit Padding am falschen Ort erntet eine Exception. Sehen Sie denselben Dekodierer an verschiedenen Eingaben in Aktion:
| Eingabe | Ergebnis |
|---|---|
"TWFu" |
Dekodiert zu Man (3 Bytes). |
"TWF\nu" (ein Zeilenumbruch in der Mitte) |
Dekodiert zu Man. Weißraum ist für den Dekodierer unsichtbar. |
"TWFu\u00A0" (ein non-breaking space am Ende) |
FormatException. Nur die vier Weißraum-Zeichen oben werden übersprungen; NBSP ist keins davon. |
"TWE" (Länge 3, kein Vielfaches von 4) |
FormatException. Die Payload-Länge, Weißraum außer Acht gelassen, muss ein Vielfaches von 4 sein. |
"TWFu=" (zusätzliches Padding hinter den Daten) |
FormatException. Höchstens zwei Padding-Zeichen, und nur ganz am Ende. |
"-_88" (URL-sicheres Alphabet) |
FormatException. Der Standard-Dekodierer kennt nur die 64 Zeichen des Standard-Alphabets. |
null |
ArgumentNullException: Value cannot be null. (Parameter 's') |
Noch eine Eigenheit, die sich auswendig zu lernen lohnt: Jede Format-Straftat bekommt dieselbe einzige Fehlermeldung, The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters. Die Meldung listet alle drei möglichen Ursachen auf und sagt nicht, welche Sie getroffen haben, und sie sagt auch nicht, wo. Wenn Sie einen fehlschlagenden Payload debuggen, zählen Sie die Zeichen, prüfen Sie das Alphabet und prüfen Sie das Padding - in dieser Reihenfolge.
Dekodieren ohne Exceptionen: Die Try-APIs
Exception-gesteuerte Ablaufsteuerung ist ein legitimes Muster, aber bei hohem Volumen oder nicht vertrauenswürdiger Eingabe ist die Try-Familie der bessere Bürger. Sie wurde in .NET Core 2.1 hinzugefügt und kommt in zwei Varianten: eine, die von einem String liest, und eine, die von einem Zeichen-Span liest. Beide schreiben in einen Puffer, den Sie bereitstellen, und melden, wie viel sie davon gefüllt haben:
using System;
using System.Text;
string payload = "TWFu"; // jeder Payload, gültig oder nicht
Span<byte> buffer = stackalloc byte[4096];
if (Convert.TryFromBase64String(payload, buffer, out int written))
{
string text = Encoding.UTF8.GetString(buffer[..written]);
Console.WriteLine(text);
}
else
{
Console.WriteLine("Not a valid Base64 payload.");
}
Zwei Verhaltensweisen machen die Try-Varianten wie eine andere Spezies wirken. Ungültige Eingabe liefert false zurück, statt etwas zu werfen, also kostet ein Strom aus fehlerhaften Payloads eine Verzweigung statt einer Exception. Eine Vorsicht: Eine null-Eingabe ist nicht Teil des Vertrags - sie wirft ArgumentNullException - also deckt die Try-Wache kaputte Payloads ab, und ein möglicherweise fehlender Wert braucht trotzdem zuerst seinen eigenen Null-Check. Die Schwestermethode Convert.TryFromBase64Chars erledigt dasselbe von einem ReadOnlySpan<char>, was praktisch ist, wenn der Payload in einem größeren Zeichenpuffer steckt und Sie nicht erst einen Teilstring herausschneiden wollen. Bemessen Sie den Ausgabe-Puffer großzügig: Die dekodierte Länge ist höchstens drei Viertel der (weißraumfreien) Eingabelänge, und der written-Out-Parameter sagt Ihnen genau, wie viel herausgekommen ist.
Span-basiertes Dekodieren mit System.Buffers.Text.Base64
Wenn Sie Allokationen zählen, oder wenn Sie wollen, dass der Dekodierer seine Fehler beschreibt, anstatt sie zu werfen, ist die Klasse System.Buffers.Text.Base64 das Werkzeug. Sie ist seit .NET Core 2.1 eine statische Klasse in der Standardbibliothek, und sie arbeitet mit Spans statt mit verwalteten Arrays. Ihre Dekodier-Methode gibt einen OperationStatus-Wert mit vier Launen zurück: Done (Erfolg), DestinationTooSmall (Ihr Puffer war zu klein), NeedMoreData (die Eingabe ist noch kein Vielfaches von 4, lesen Sie weiter) und InvalidData (das ist kein Base64). Der letzte boolesche Parameter, isFinalBlock, ist das, was diese beiden unterscheidet: Er sagt dem Dekodierer, ob noch mehr Eingabe kommt. Hier ist die One-Shot-Form, bemessen mit dem eigenen Helper der Klasse:
using System.Buffers;
using System.Buffers.Text;
using System.Text;
string payload = "TWFu";
byte[] input = Encoding.ASCII.GetBytes(payload);
byte[] output = new byte[Base64.GetMaxDecodedFromUtf8Length(input.Length)];
OperationStatus status = Base64.DecodeFromUtf8(input, output,
out int consumed, out int written, isFinalBlock: true);
if (status == OperationStatus.Done)
{
Console.WriteLine(Encoding.UTF8.GetString(output.AsSpan(0, written)));
// Man
}
Zwei weitere Mitglieder dieser Klasse verdienen einen Absatz. Das erste ist IsValid, das einen Payload prüft, ohne ihn zu dekodieren. Es kommt in Byte-Span- und Zeichen-Span-Varianten, und eine Overload meldet die dekodierte Länge neben dem Urteil, also können Sie einen Puffer aus einer einzigen Prüfung bemessen:
using System.Buffers.Text;
string payload = "TWFu";
if (Base64.IsValid(payload, out int decodedLength))
{
Console.WriteLine("Valid, decodes to " + decodedLength + " bytes.");
// Valid, decodes to 3 bytes.
}
else
{
Console.WriteLine("Rejecting payload before allocating anything.");
}
Das zweite ist DecodeFromUtf8InPlace, für die Situation, in der der Base64-Text bereits in einem Puffer sitzt, den Sie besitzen, und Sie nichts dagegen haben, ihn zu überschreiben. Dekodieren macht die Daten kleiner, also wird das Ergebnis an den Anfang desselben Puffers geschrieben, und die Methode meldet, wie lang es ist:
using System.Buffers;
using System.Buffers.Text;
using System.Text;
byte[] data = Encoding.ASCII.GetBytes("TWFu");
OperationStatus status = Base64.DecodeFromUtf8InPlace(data, out int written);
if (status == OperationStatus.Done)
{
Console.WriteLine(Encoding.ASCII.GetString(data, 0, written));
// Man, jetzt zu Hause in den ersten drei Bytes desselben Puffers
}
Ein Verhalten, das sich in der Tasche zu behalten lohnt: Diese Klasse überspringt ebenfalls die vier normalen Weißraum-Zeichen (Leerzeichen, Tab, Zeilenumbruch, Wagenrücksetzer), also dekodiert ein zeilenweise umwickelter Payload genauso gut. Sie ist streng in dem, was zählt: Ein Payload, dessen weißraumfreie Länge kein Vielfaches von vier ist, ist InvalidData, wenn es der finale Block ist, und Zeichen außerhalb des Standard-Alphabets werden glatt abgewiesen. Irgendwo in dieser Klasse gibt es kein stillschweigendes Aufräumen.
URL-sicheres Base64: Die Base64Url-Klasse
Für dieselben 64 Werte existiert ein zweites Alphabet, und in der C#-Webarbeit werden Sie ständig darauf treffen. Im Standard-Alphabet sind die Werte 62 und 63 + und /, zwei Zeichen, die in URLs Ärger machen: Ein + in einem Query-String wird routinemäßig als Leerzeichen dekodiert, und / und = brauchen jeweils eine Prozent-Kodierung. RFC 4648, Abschnitt 5, behebt das, indem es - und _ einschiebt, die in keinem URL-Kontext eine Sonderbedeutung haben, und macht das nachgestellte =-Padding optional. Das Ergebnis heißt base64url, und es ist das Alphabet von JWTs, API-Tokens, Datei-Upload-IDs und unzähligen URLs (YouTubes 11-stellige Video-Identifikatoren sind base64url ohne Padding).
Seit .NET 9 liefert die Standardbibliothek eine dedizierte Klasse dafür: System.Buffers.Text.Base64Url. Sie ist der URL-sichere Zwillingsbruder der Base64-Klasse, mit eigenen Dekodier-, Validier- und Längen-Helpers:
using System.Buffers.Text;
using System.Text;
string token = "-__8";
byte[] bytes = Base64Url.DecodeFromChars(token);
Console.WriteLine(BitConverter.ToString(bytes));
// FB-FF-FC
Beachten Sie, was die klassische API mit diesem Beispiel nicht geschafft hätte. Dieselben drei Bytes kodieren im Standard-Alphabet als +//8, und Convert.FromBase64String("+//8") funktioniert, aber Convert.FromBase64String("-__8") wirft, denn die URL-sicheren Zeichen sind außerhalb ihres Alphabets. Und base64url-Payloads kommen häufig ohne Padding an, was der klassische Dekodierer ebenfalls ablehnt, denn er besteht auf der vollen Gruppe zu viert. Die Base64Url-Klasse behandelt beide Varianten des Problems nativ: Sie dekodiert TWE (drei Zeichen, kein Padding) zu den zwei Bytes Ma, und sie dekodiert TWE= genauso gut.
Wenn Ihr Projekt auf einer älteren Runtime läuft, gibt es zwei praktische Wege. Auf .NET Framework 4.6.2 und höher fügen Sie das Microsoft.Bcl.Memory-NuGet-Paket hinzu, das Microsoft gezielt publiziert, um Base64Url zurückzuporten (zusammen mit einigen anderen modernen Typen):
dotnet add package Microsoft.Bcl.Memory
Oder, ganz ohne Paket: normalisieren Sie den Payload, bevor Sie ihn dem klassischen Dekodierer geben: tauschen Sie die URL-sicheren Zeichen zurück in ihre Standard-Zwillinge, und füllen Sie das fehlende Padding auf. Dieser kleine Helper ist der häufigste selbst gebaute base64url-Dekodierer im C#-Code, und er lohnt sich zu kennen, denn er funktioniert auf jeder Runtime seit .NET Framework 1.1:
using System;
using System.Text;
string segment = "TWE";
segment = segment.Replace('-', '+').Replace('_', '/');
segment += new string('=', (4 - segment.Length % 4) % 4);
byte[] bytes = Convert.FromBase64String(segment);
Console.WriteLine(Encoding.ASCII.GetString(bytes));
// Ma
Die Formel (4 - length % 4) % 4 ist die gesamte Padding-Rechnung: Sie fügt null, eins oder zwei =-Zeichen hinzu, damit die Länge auf ein Vielfaches von vier landet, und das äußere Modulo verhindert, dass bereits gepaddete Eingabe zusätzliche Zeichen erhält.
Von Bytes zu Wörtern: Text, Unicode und Zeichensätze
Das Dekodieren liefert Bytes, und Bytes sind eine völlig neutrale Sache. Sie werden erst zu "Text", wenn Sie einen Zeichensatz wählen, als den Sie sie lesen, und diese Wahl trifft allein Sie, denn Base64 trägt keine Information darüber, welchen Zeichensatz der ursprüngliche Autor verwendet hat. In der Praxis bedeutet das: Gehen Sie von UTF-8 aus, es sei denn, Sie haben einen Grund, es nicht zu tun, und schreiben Sie es im Code explizit, denn ein expliziter Encoding.UTF8-Aufruf ist der Unterschied zwischen einem Programm, das zufällig korrekt ist, und einem, das per Design korrekt ist:
using System;
using System.Text;
string original = "h\u00e9llo \u4e16\u754c";
byte[] utf8 = Encoding.UTF8.GetBytes(original);
string packed = Convert.ToBase64String(utf8);
byte[] decoded = Convert.FromBase64String(packed);
string restored = Encoding.UTF8.GetString(decoded);
Console.WriteLine(restored == original);
// True: h\u00e9llo \u4e16\u754c roundtript perfekt
Die subtile Falle ist das, was passiert, wenn die Bytes kein gültiges UTF-8 sind, weil der Payload wirklich Latin-1 war, oder binär, oder einfach beschädigt. Standardmäßig ersetzt der UTF-8-Dekodierer von .NET jede fehlerhafte Sequenz durch den Unicode-Ersatzzeichner (U+FFFD) und macht weiter. Keine Exception, keine Warnung: Die Daten sind einfach weg, verwandelt in Fragezeichen in Ihrer Datenbank. Wenn Sie wissen wollen, wann das passiert, konstruieren Sie die Encoding mit einem strengen Fallback, der die stille Ersetzung in eine laute DecoderFallbackException verwandelt:
using System.Text;
byte[] bytes = Convert.FromBase64String("//4="); // die Bytes FF FE, kein gültiges UTF-8
Encoding strictUtf8 = Encoding.GetEncoding(
"utf-8",
new EncoderExceptionFallback(),
new DecoderExceptionFallback());
string text = strictUtf8.GetString(bytes);
// Wirft DecoderFallbackException, denn FF FE ist keine UTF-8-Sequenz
Für Payloads, bei denen Sie lieber überleben als scheitern, sind die Ersetzungs-Fallbacks die mildere Option, und den Ersetzungs-Text wählen Sie selbst:
using System.Text;
byte[] bytes = Convert.FromBase64String("//4="); // die Bytes FF FE, kein gültiges UTF-8
Encoding forgivingUtf8 = Encoding.GetEncoding(
"utf-8",
EncoderFallback.ReplacementFallback,
new DecoderReplacementFallback("[bad]"));
string text = forgivingUtf8.GetString(bytes);
Console.WriteLine(text);
// [bad][bad] statt der stillen U+FFFD-Ersetzung
Noch eine C#-spezifische Geschichtslektion: Encoding.Default bedeutet auf verschiedenen Runtimes verschiedene Dinge. Auf .NET Framework unter Windows ist es die ANSI-Zeichensatzseite des Systems (oft Windows-1252), während es auf .NET (Core) UTF-8 ohne BOM ist. Code, der einen Payload über Encoding.Default roundtript, kann daher auf einer Maschine von 2010 andere Bytes produzieren als auf einer von 2025, und Base64 kodiert fröhlich das, was ihm hingegeben wird. Wenn Sie je einen dekodierten String voller Kauderwelsch mit falschen Akzenten sehen, ist Encoding.Default der erste Ort, an den Sie schauen sollten.
Dateien und binäre Payloads
Dateien sind das geradlinigste Dekodier-Ziel, denn hier gibt es gar keine Zeichensatz-Frage: Die Bytes, die Sie dekodieren, sind die Datei, Byte für Byte, Nullen inklusive. Das Muster: zwei Aufrufe und eine Datei, und es taucht in allem auf, von Bild-Uploads bis zu Backup-Tools:
using System.IO;
string b64 = File.ReadAllText("payload.b64");
byte[] original = Convert.FromBase64String(b64);
File.WriteAllBytes("restored.bin", original);
Console.WriteLine("Restored " + original.Length + " bytes.");
Zwei praktische Hinweise. Wenn die Datei Weißraum oder Zeilenumbrüche enthalten kann (was sie als Textdatei mit ziemlicher Sicherheit tut), macht der klassische Dekodierer das kostenlos mit, wie Sie weiter oben gesehen haben. Und wenn der Payload groß ist, gehen Sie gar nicht erst über einen String: Lassen Sie den Schritt von der Datei in einen String aus und dekodieren Sie direkt aus dem Stream - das ist das Thema des nächsten Abschnitts. Für einen dekodierten Payload, der Text ist und dessen Zeichensatz Sie zufällig kennen, ist das Datei-Beispiel die komplette Lösung, und der Encoding.UTF8.GetString-Schritt aus dem Zeichensatz-Abschnitt passt genau zwischen Dekodieren und Verwendung.
Dekodieren aus einem Stream: FromBase64Transform
Die Convert-Methoden sind für Payloads ausgelegt, die in einen String passen, und die offizielle Dokumentation sagt es mit diesen Worten: Für Streaming-Daten verwenden Sie die Transform-Klassen. FromBase64Transform ist seit .NET Framework 1.1 (2003) Teil von System.Security.Cryptography, und es steckt in CryptoStream, dem universellen Schlauch des Frameworks zum Verändern von Daten, während sie fließen. Das komplette Datei-zu-Datei-Dekodieren ist ein Setup aus vier Zeilen:
using System.IO;
using System.Security.Cryptography;
using FileStream source = File.OpenRead("payload.b64");
using FromBase64Transform transform =
new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces);
using CryptoStream reader = new CryptoStream(source, transform, CryptoStreamMode.Read);
using FileStream target = File.Create("payload.bin");
reader.CopyTo(target);
Console.WriteLine("Done, " + target.Length + " bytes written.");
Der Konstruktor nimmt einen Modus, und die zwei Modus lohnen es, nach Namen zu kennen. IgnoreWhiteSpaces (der Standard, passend zur Weißraum-Politik des klassischen Dekodierers) überspringt die vier normalen Weißraum-Zeichen, während der Stream fließt, und genau das wollen Sie für per E-Mail umgewickelte oder zeilenumbruchgespickte Payloads. DoNotIgnoreWhiteSpaces ist streng: Das erste Zeichen außerhalb des Alphabets, das es trifft, wirft eine FormatException, und genau das wollen Sie, wenn ein irrtümliches Leerzeichen im Payload ein Bug sein sollte und keine Schulterzucker-Geste. Unter der Haube verarbeitet die Transform die Eingabe in Gruppen zu vier Zeichen und gibt die drei Bytes zurück, die jede Gruppe produziert, wobei TransformFinalBlock den Rest abarbeitet. Sie rufen diese Methoden selten selbst auf, denn CryptoStream erledigt das für Sie, aber die Vierergruppen-Tatsache zählt: Wenn Sie die Transform je manuell füttern, füttern Sie sie in Vielfachen von vier, sonst sitzt die letzte teilweise Gruppe im finalen Block.
JWTs: Drei Segmente, ein Punkt
Ein JSON Web Token ist der meistgenutzte base64url-Payload in der C#-Webentwicklung, und seine Form ist täuschend einfach: drei durch Punkte getrennte Segmente. Das erste ist der kodierte Header, das zweite der kodierte Payload (auch: claims), und das dritte die Signatur. Jeweils die ersten beiden sind base64url eines UTF-8-JSON-Dokuments, ohne Padding, gemäß der JWS-Spezifikation. Aufteilen und Dekodieren sind zwei Zeilen C#:
using System;
using System.Buffers.Text;
using System.Text;
using System.Text.Json;
string jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsIm5hbWUiOiJBZGEifQ.c2lnbmF0dXJl";
string[] parts = jwt.Split('.');
string headerJson = Encoding.UTF8.GetString(Base64Url.DecodeFromChars(parts[0]));
string payloadJson = Encoding.UTF8.GetString(Base64Url.DecodeFromChars(parts[1]));
using JsonDocument doc = JsonDocument.Parse(payloadJson);
Console.WriteLine(doc.RootElement.GetProperty("name").GetString());
// Ada
Auf Runtimes vor .NET 9 läuft derselbe Job über den Normalisierungs-Helper aus dem URL-sicheren Abschnitt: - und _ zurück zu + und / tauschen, das Segment auf ein Vielfaches von vier auffüllen und mit Convert.FromBase64String dekodieren. Beide Wege liefern dasselbe JSON; wählen Sie den, der zu Ihrem Zielframework passt.
Eine Grenze, die scharf zu halten ist: Ein JWT zu dekodieren ist nicht, ein JWT zu verifizieren. Das Dekodieren oben liest fröhlich die claims eines Tokens mit Müll-Signatur, denn die Signatur ist ein separates kryptografisches Prüfverfahren über die ersten beiden Segmente. Für Token-Arbeit im Produktivbetrieb: nicht von Hand parsen. Das System.IdentityModel.Tokens.Jwt-Paket (aus der Microsoft.IdentityModel-Familie) macht Parsen, Validierung und Ablauf in einem, und seine base64url-Behandlung ist exakt das Alphabet, das dieser Abschnitt beschreibt. Dekodieren Sie von Hand zum Debuggen und für kleine Werkzeuge; verifizieren Sie mit der Bibliothek alles, was ein Nutzer erreichen kann.
Data-URIs und eingebettete Bilder
Es gibt eine ganze Klasse an C#-Code, dessen Job es ist, eine data:-URI zu empfangen, denn HTML, CSS und unzählige Web-APIs verwenden sie, um binäre Inhalte inline einzubetten. Das Scheme, standardisiert durch RFC 2397, lautet data:[mediatype][;base64],payload: Alles vor dem ersten Komma sind Metadaten (der MIME-Typ und die ;base64-Flagge), alles danach ist der Payload. Wenn die ;base64-Flagge vorhanden ist, ist der Payload ein Base64-String, und das Aufteilen am Komma ist die gesamte Analyse:
using System;
using System.Text;
string dataUri = "data:image/png;base64,iVBORw0KGgo=";
int comma = dataUri.IndexOf(',');
string mediaType = dataUri[..comma]; // data:image/png;base64
string b64 = dataUri[(comma + 1)..]; // iVBORw0KGgo=
byte[] imageBytes = Convert.FromBase64String(b64);
Console.WriteLine(imageBytes.Length);
// 8: die PNG-Signatur-Bytes 89 50 4E 47 0D 0A 1A 0A
Das Präfix iVBORw0KGgo= im Beispiel ist die Base64-Form der achttelligen PNG-Magic-Number, und es ist ein nützlicher Fingerabdruck: Jede Data-URI für ein echtes PNG beginnt so, also ist es ein schneller Plausibilitäts-Check, wenn Sie nicht vertrauenswürdige HTML parsen. Zwei praktische Hinweise für C#-Entwickler. Erstens versteht die Uri-Klasse Data-URIs nativ auf .NET: new Uri("data:text/plain;base64,TWFu") parst problemlos und meldet Scheme == "data", also wenn Ihr Code anhand von URIs routet, werden Data-URIs in der Pipeline auftauchen, und Sie sollten entscheiden, wie Sie sie behandeln. Zweitens: Denken Sie daran, was eine Data-URI wirklich ist: eine vollständige Kopie der Datei, um ein Drittel aufgeblasen, die in Ihrem Dokument sitzt. Das ist in Ordnung für ein 4-kB-Favicon und schmerzhaft für ein 4-MB-Logo, also wenn Sie sie selbst generieren (der Kodierungs-Artikel deckt diese Seite ab), machen Sie das Bild kleiner, bevor Sie es kodieren.
HTTP: Basic Auth und API-Austausche
Base64 ist in HTTP an mindestens einer Stelle verwebt, die Sie in jeder API-Arbeit berühren werden: das Basic-Authentifizierungsschema. Der Client sendet Authorization: Basic gefolgt von der Base64-Kodierung von username:password, verbunden durch einen Doppelpunkt. Auf der Server-Seite bedeutet das Dekodieren eines eingehenden Headers daher: Das Basic -Präfix abtrennen, dekodieren und am ersten Doppelpunkt aufteilen:
using System;
using System.Text;
string header = "Basic YWRhOnMzY3JldA==";
string encoded = header["Basic ".Length..].Trim();
string credentials = Encoding.UTF8.GetString(Convert.FromBase64String(encoded));
int colon = credentials.IndexOf(':');
string user = credentials[..colon];
string password = credentials[(colon + 1)..];
Console.WriteLine(user); // ada
Console.WriteLine(password); // s3cret
Der UTF-8-Schritt ist wichtiger, als er aussieht: RFC 7617 fixiert den Zeichensatz nicht, lässt den Standardwert aus Kompatibilitätsgründen undefiniert und erlaubt nur einen empfehlenden UTF-8-Hinweis, aber genau diesen Hinweis erwarten alle modernen Server, also produziert ein Benutzername mit einem Umlaut ein anderes (und korrektes) Byte-String als derselbe Benutzername, gelesen als Latin-1. Die Dekodier-Seite von Basic Auth ist das einfache Ende dieses Musters; in ASP.NET Core treffen Sie es normalerweise über die Authentifizierungs-Handler und nicht über rohe Headers, aber dieselbe Dekodier-Logik ist es, die darunter läuft, und genau die Art Code, die Sie brauchen, wenn Sie Integrationstests schreiben, die einen API-Server vortäuschen. Die spiegelbildliche Operation, den Header auf der Client-Seite zu bauen, ist ein Einzeiler auf der Kodier-Seite, und er bekommt ein vollständiges Beispiel im Kodierungs-Artikel.
E-Mail: MIME und zeilenweise umwickelte Payloads
E-Mail ist der Ort, an dem Base64 seinen Ruf erworben hat, und sie ist immer noch die Quelle vieler Payloads, die C#-Dienste empfangen. SMTP war ursprünglich ein 7-Bit-Protokoll, daher können binäre Anhänge nicht roh reisen: Die MIME-Spezifikation (RFC 2045) kodiert sie als Base64 mit einem Content-Transfer-Encoding: base64-Header, bricht die Ausgabe bei 76 Zeichen um und trennt die Zeilen mit Paaren aus Wagenrücksetzer und Zeilenumbruch. Ein echter Anhang-Body sieht daher aus wie eine Spalte aus 76-Zeichen-Zeilen, und die gute Nachricht für C# ist, dass der klassische Dekodierer das schon lesen kann: Da er Weißraum überall im String überspringt, können Sie ihm den ganzen umgewickelten Body geben, Zeilenumbrüche inklusive, und er dekodiert ihn, als wären die Zeilenumbrüche nie da gewesen:
using System;
using System.Text;
string attachmentBody = "TWFu\r\nTWFu\r\nTWFu";
byte[] bytes = Convert.FromBase64String(attachmentBody);
Console.WriteLine(Encoding.ASCII.GetString(bytes));
// ManManMan
Für Payloads, die über einen Stream ankommen statt über einen String, ist die FromBase64Transform mit ihrem Weißraum-ignorierenden Modus dieselbe Geschichte in Streaming-Kleidung. Und wenn Sie mehr tun müssen, als den Body zu dekodieren, wenn Sie die MIME-Struktur durchwandern, Headers parsen, verschachtelte Multipart-Abschnitte behandeln oder jeden Anhang aus einer echten .eml-Datei extrahieren müssen, ist die Ökosystem-Antwort in C# das MimeKit-Paket: Es ist die Standard-MIME-Bibliothek für .NET, es behandelt die Content-Transfer-Kodierungen Base64 und quoted-printable intern, und es ist das Werkzeug, zu dem Sie greifen, sobald "einfach nur den Body dekodieren" aufhört, Ihr Problem zu beschreiben. Die eigene MailMessage-Klasse des Frameworks dekodiert einfache Anhänge für Sie, aber ihr MIME-Support ist nach modernen Maßstäben bewusst bescheiden.
PEM-Zertifikate
PEM ist das Rüstungsformat der TLS-Welt: ein Base64-Body zwischen -----BEGIN CERTIFICATE------ und -----END CERTIFICATE------Markern, umgewickelt bei 64 Zeichen, wie von RFC 7468 spezifiziert. C#-Entwickler treffen es als die Zertifikats-Dateien hinter jedem HTTPS-Endpunkt, und die Dekodier-Geschichte hier ist besser, als Sie vielleicht erwarten, denn seit .NET 6 parst das Framework PEM für Sie, Base64-Body inklusive:
using System.IO;
using System.Security.Cryptography.X509Certificates;
string pem = File.ReadAllText("server.pem");
X509Certificate2 certificate = X509Certificate2.CreateFromPem(pem);
Console.WriteLine(certificate.Subject);
// CN=server.example.com
Kein manuelles Base64 irgendwo darin: CreateFromPem findet die Marker, wickelt den Body aus, dekodiert ihn und gibt Ihnen ein lebendiges Zertifikat zurück. (Die Familie hat Geschwister für private Schlüssel und für die kombinierte Zertifikat-plus-Schlüssel-Form, falls Ihre Infrastruktur Ihnen die in die Hand drückt.) Wenn Sie auf einer älteren Runtime sind, oder Sie die rohen DER-Bytes brauchen, die in der Rüstung sitzen, ist die manuelle Version ein Zwei-Schritte-Abstreifen-und-Dekodieren, und sie lohnt sich zu kennen, denn dasselbe Muster funktioniert für alles, was in PEM-Rüstung steckt:
using System;
using System.Text;
string pem = File.ReadAllText("server.pem");
string body = pem
.Replace("-----BEGIN CERTIFICATE-----", "")
.Replace("-----END CERTIFICATE-----", "")
.Replace("\r", "")
.Replace("\n", "");
byte[] der = Convert.FromBase64String(body);
Console.WriteLine(der.Length);
// Die Länge des DER-Zertifikats innerhalb der Rüstung
Die Fallen in dieser Ecke sind allesamt Weißraum: PEM-Dateien tragen CRLF-Zeilenenden von den meisten Zertifikats-Tools, also streichen Sie vor dem Dekodieren sowohl \r als auch \n weg, nicht nur die Zeilenumbrüche. Und verwechseln Sie den Zertifikats-Body nicht mit einem privaten Schlüssel-Body, der andere Marker und andere Inhalte hat; ein Dekodierer rettet Sie nicht davor.
Konfiguration, Umgebungsvariablen und Datenbanken
Das dritte Zuhause von Base64 in C#-Anwendungen ist der Speicher: Config-Dateien, Umgebungsvariablen und Datenbankspalten. Das Muster ist überall dasselbe. Ein binärer oder geheimer Wert wird auf dem Weg hinein in einen String kodiert und auf dem Weg hinaus wieder zu Bytes dekodiert. Umgebungsvariablen sind das sichtbarste Beispiel, denn sie können nur Text halten:
using System;
using System.Text;
string? encoded = Environment.GetEnvironmentVariable("API_KEY_B64");
if (encoded == null)
{
throw new InvalidOperationException("Set the API_KEY_B64 environment variable first.");
}
byte[] keyBytes = Convert.FromBase64String(encoded);
string apiKey = Encoding.UTF8.GetString(keyBytes);
Console.WriteLine(apiKey.Length + " characters of API key, ready to use.");
In einer Datenbank taucht dieselbe Idee normalerweise als byte[]-Eigenschaft auf, die Sie aus Portabilitätsgründen in einer Textspalte speichern wollen, und Entity Framework Core hat genau dafür einen eingebauten Mechanismus: einen Value-Converter, der Ihre Kodier- und Dekodier-Funktionen transparent bei jedem Lesen und Schreiben ausführt:
using Microsoft.EntityFrameworkCore;
modelBuilder.Entity<Avatar>()
.Property(a => a.ImageData)
.HasConversion(
v => Convert.ToBase64String(v),
v => Convert.FromBase64String(v));
Dies ein einziger Converter ist die gesamte Datenbank-Integration: ImageData bleibt in Ihrem C#-Code ein byte[], und die Datenbank sieht einen Base64-String. Zwei Warnungen gehören in diesen Abschnitt. Erstens hält eine Spalte gegebener Breite als kodierter Text etwa ein Drittel weniger Daten als dieselbe Breite als rohes Binär, wegen der Steuer von vier Zeichen pro drei Bytes, also bemessen Sie die Spalte für die kodierte Länge, wenn sie eine feste Breite hat. Zweitens, und das ist der Sicherheits-Teil: Base64 in einer Config-Datei ist eine Bequemlichkeit, um einen Wert in einer einzigen Zeile zu halten, kein Schutz für den Wert. Wer die Config-Datei lesen kann, kann den Key in einem einzigen Befehl dekodieren, deshalb gehören echte Geheimnisse in einen Secret Store, und das Base64 dort ist nur das Transportformat.
Wenn der Payload groß ist
Base64-Dekodieren hat eine angenehme Eigenschaft, die das Kodieren nicht hat: Die Ausgabe ist immer kleiner als die Eingabe, ungefähr drei Viertel davon. Ein 10-Megabyte-Text-Payload dekodiert zu etwa 7,5 Megabyte Bytes, also kann ein Dekodieren Ihren Speicher nie aufblähen wie ein Kodieren. Die Arithmetik, falls Sie einen Puffer von vornherein bemessen müssen, kommt auf einen von zwei Aufrufen hinaus: Base64.GetMaxDecodedFromUtf8Length für die strenge Span-Klasse, oder die schlichte Division, length / 4 * 3 für die klassische API, plus einen Spielraum für Weißraum, wenn die Eingabe umgewickelt ist. (Der Helper gibt die maximal mögliche dekodierte Länge zurück: Die echte Länge ist genau dann so groß, wenn die letzte Gruppe kein Padding hat, und ein oder zwei Bytes kleiner, wenn sie mit ein oder zwei Padding-Zeichen endet.)
Wenn der Payload aber wirklich groß ist, ist der richtige Zug nicht ein größerer Puffer - es ist gar kein Puffer: Lassen Sie den String komplett aus und lassen Sie FromBase64Transform das Dekodieren von der Quelle zum Ziel streamen, wie im Streaming-Abschnitt gezeigt. Die einzige Regel, die einzuhalten ist, ist die Vierergruppen-Ausrichtung: Ein Base64-Stream kann nur an Vielfachen von vier Zeichen angeschnitten werden (nachdem der Weißraum berücksichtigt ist), also wenn Sie die Transform von Hand füttern, lesen Sie in Chunks, die Vielfache von vier sind, und lassen Sie TransformFinalBlock den Rest abfließen. Für alles unter einigen hundert Megabyte ist das One-Shot-Dekodieren schnell genug, dass dies eine Optimierung ist, keine Notwendigkeit, aber die Streaming-Form ist auch die, die sich unter Speichergrenzen gut verhält, und genau die sind die Umgebungen, in denen große Payloads gerne leben.
Ein Dekodierer in Ihrem Terminal
Es gibt in jeder Sprache einen befriedigenden Moment, in dem ein 15-zeiliges Konsolenprogramm ein Kommandozeilen-Tool wird, und C#'s Base64-Dekodierer ist ein guter Kandidat dafür, denn das Lesen von Standard-Input macht es zum Drop-in für Shell-Pipes. Hier ist das ganze Tool: Es liest den Base64-Payload aus der Pipe (oder aus einem Argument), dekodiert ihn und schreibt die rohen Bytes in eine Datei:
using System;
using System.IO;
using System.Text;
string input = args.Length > 0 ? File.ReadAllText(args[0]) : Console.In.ReadToEnd();
byte[] bytes = Convert.FromBase64String(input.Trim());
File.WriteAllBytes("output.bin", bytes);
Console.Error.WriteLine("Wrote " + bytes.Length + " bytes to output.bin.");
Bauen Sie es einmal, und es steht neben der eigenen base64-Nutzlichkeit der Shell für die Tage, an denen Sie spezifisch den Dekodierer der .NET-Runtime wollen: Leiten Sie eine Datei hindurch, verketteten Sie es mit anderen Tools, und die strengen C#-Prüfregeln (weißraum-tolerant, alphabet-streng, padding-streng) werden Teil Ihrer Pipeline. Das Trim() macht dort stille Arbeit und fängt den nachgestellten Zeilenumbruch ab, den Texteditoren so gerne anhängen, obwohl der Dekodierer ihn fairerweise ignoriert hätte. Für die URL-sicheren Payloads, die immer häufiger in API-Logs auftauchen, ist dasselbe Gerüst mit dem Base64Url-Dekodieren aus dem URL-sicheren Abschnitt die gesamte Änderung.
Tempo: Womit Sie rechnen können
Base64 in modernem .NET ist schnell, und es wird immer schneller. Die Runtime-Implementierungen sowohl der Convert-Methoden als auch der System.Buffers.Text-Klassen sind mit SIMD-Vektoranweisungen optimiert, wo die Hardware sie unterstützt, und sie verarbeiten viele Zeichen pro Takt. In der Praxis bedeutet das: Mehr-Megabyte-Payloads dekodieren in einstelligen bis niedrigen zweistelligen Millisekunden auf einer gewöhnlichen Desktop-Maschine, was schnell genug ist, dass Base64-Dekodieren in jeder Anwendung, die Sie schreiben, effektiv umsonst ist. Der praktische Performance-Rat dreht sich also um die Form Ihres Codes, nicht um den Dekodierer selbst. Bevorzugen Sie auf heißen Pfaden die Try-Methoden oder die status-gebenden Span-Methoden, wo fehlerhafte Eingabe möglich ist und Exceptionen teuer wären. Wiederverwenden Sie Puffer mit den In-Place- und Span-APIs, wenn Sie Tausende kleiner Payloads in einer Schleife dekodieren, anstatt pro Aufruf ein frisches Array zu allozieren. Und dekodieren Sie denselben Payload nie zweimal: Einmal ist der Preis, und ein zweites Dekodieren eines Felds, das Sie schon dekodiert haben, ist reine Verschwendung, die in Profilen als zweiter mysteriöser Base64-Spike auftaucht.
Sicherheit: Was Base64 nicht tut
Die wichtigste Sicherheits-Tatsache über Base64 ist die, die Anfänger am häufigsten übersehen: Es ist Kodierung, keine Verschlüsselung. Ein Base64-String ist für jeden lesbar, mit jedem Tool, in einem Bruchteil einer Sekunde, und C# macht das Lesen daraus einen Einzeiler, wie dieser gesamte Artikel gezeigt hat. Base64 hat keinen Key, keinen Algorithmus-Parameter und keine Schwäche, die auszunutzen wäre, denn es hat nie versucht, irgendetwas zu verstecken: Es ist ein Transportformat, ein Weg, Binäres in text-only-Kanälen zu überleben. Behandeln Sie es entsprechend. Legen Sie nie ein Passwort, ein Token oder ein Geheimnis in eine Config-Datei, die durch Base64 "geschützt" ist, denn der Schutz ist genau einen Convert.FromBase64String-Aufruf tief. Wenn der Wert geheim sein muss, braucht er echten Schutz (einen Secret Manager, einen verschlüsselten Store, mindestens eine Betriebssystem-Zugriffskontrolle), und das Base64 ist nur die Form, die es trägt, während es reist.
Der zweite Sicherheitshinweis betrifft Ihren eigenen Dekodier-Pfad. Jeder Payload, den Sie dekodieren, ist nicht vertrauenswürdige Eingabe, bis das Gegenteil bewiesen ist, und die zwei Fehlerszenarien, für die Sie designen, sind das laute (ungültige Eingabe, auf die die klassische API mit einer FormatException antwortet, die Sie fangen und in eine 400 umwandeln sollten, nicht in eine 500) und das stille (gültiges Base64, das zu Bytes dekodiert, die nicht das sind, was Sie erwartet haben: nicht UTF-8, nicht der Dateityp, den Sie verlangt haben, oder länger als Ihr Budget). Prüfen Sie, bevor Sie vertrauen: Prüfen Sie die Länge mit IsValid oder der Try-Familie, bevor Sie allozieren, prüfen Sie die dekodierten Bytes gegen eine erwartete Signatur (die PNG-Magic, den PKCS-Header), bevor Sie sie an einen Bild- oder Zertifikats-Parser geben, und bemessen Sie Ihre Puffer aus der kodierten Länge, bevor Sie dekodieren, nicht danach. Base64 dekodiert alles, was wohlgeformt ist; zu entscheiden, was wohlgeformt für Ihre Anwendung bedeutet, ist Ihr Job.
Fallen, die Sie kennen sollten, bevor sie beißen
Das sind die C#-spezifischen Fallen, die immer wieder in echtem Code auftauchen, und jede einzelne hat eine konkrete Ursache darin, wie das Framework arbeitet:
- Binäres durch einen String. Ein C#-
stringist eine Folge von UTF-16-Codeeinheiten, und dekodiertes Base64 ist es nicht. In dem Moment, in dem Sie dekodierte Bytes in eine String-Variable stopfen (einConsole.WriteLineeines dekodierten PNGs, eine String-Konkatenation mit Binärem, eine JSON-Bibliothek, die "Text" serialisiert), wird etwas weiter unten den Strang zerknüllen. Halten Sie dekodiertes Binäres inbyte[], bis es an einen Ort gelangt, der Bytes wirklich will. - Die Encoding.Default-Spaltung. Code, der dekodierte Bytes mit
Encoding.Defaultliest, produziert unterschiedlichen Text auf .NET Framework (der Windows-ANSI-Zeichensatz) und auf .NET (UTF-8). Derselbe Payload, zwei unterschiedliche Ausgaben, keine Exception. Fixieren Sie Ihre Encoding explizit. - JWT-Segmente und der klassische Dekodierer. Ein rohes JWT-Segment in
Convert.FromBase64Stringzu füttern scheitert auf zweierlei Art gleichzeitig: Die-/_-Zeichen sind außerhalb des Standard-Alphabets, und das fehlende Padding bricht die Längenregel. Normalisieren Sie zuerst, oder verwenden SieBase64Url. - Weißraum, den Sie sehen, und Weißraum, den Sie nicht sehen. Der Dekodierer überspringt Leerzeichen, Tab, Zeilenumbruch und Wagenrücksetzer, und überspringt nichts anderes. Ein non-breaking space, ein Unicode-Zeilengetrenner oder ein vertikaler Tab in einem Payload (alles überlebt Copy-Paste von einigen Webseiten) ist eine
FormatException, keine Schulterzucker-Geste. - Eine Fehlermeldung für jede Straftat. Die
FormatExceptionvom klassischen Dekodierer sagt nicht, welche Regel gebrochen wurde oder wo. Debuggen Sie, indem Sie Länge, dann Alphabet, dann Padding prüfen, in dieser Reihenfolge, oder wechseln Sie zuTryFromBase64StringundIsValidfür eine boolesche Antwort. - Stille UTF-8-Ersetzung.
Encoding.UTF8.GetStringverwandelt fehlerhafte Byte-Folgen ohne Murren in U+FFFD. Wenn der Payload möglicherweise kein gültiges UTF-8 ist, verwenden Sie den strengen Fallback aus dem Zeichensatz-Abschnitt, oder Sie werden Wochen nach dem Vorfall fehlende Daten untersuchen. - Stream-Schnitt am falschen Ort. Ein Base64-Stream kann nur an Vielfachen von vier Zeichen geschnitten werden. Chunken Sie ein Streaming-Dekodieren an einer anderen Grenze, und die letzte teilweise Gruppe landet in
TransformFinalBlock, wo sie entweder hingehört oder Ihre Ausrichtungs-Buchhaltung kaputt macht. - PEM-Zeilenenden. Zertifikats-Dateien tragen CRLF. Streichen Sie beim manuellen Auswickeln der Rüstung
\rebenso wie\nweg, oder die erste Zeile Ihres "dekodierten" DER ist ein Wagenrücksetzer in einem Daten-Byte-Kleid. - Doppelte Kodierung. Wenn ein Payload bereits Base64 war, als er Sie erreichte (eine Config, die einen Base64-String Base64-kodiert hat, eine API, die die Ausgabe eines anderen Encoders kodiert hat), liefert ein Dekodieren Ihnen mehr Base64, nicht Ihre Daten. Der Round-Trip schließt sich erst nach so vielen Dekodierungen, wie es Kodierungen gab, und die Encoder-Seite dieses Bugs ist das Thema des Kodierungs-Artikels.
Eine kurze Geschichte von Base64 in C#
Die Base64-Geschichte in C# ist auch eine Geschichte des Heranwachsens der .NET-Plattform, und sie reicht weiter zurück, als die meisten erwarten:
- .NET Framework 1.1, April 2003.
Convert.FromBase64Stringund seine Geschwister kommen, und sie tragen das Design, das die API bis heute definiert: streng beim Alphabet, großzügig bei den vier Weißraum-Zeichen, ungeschliffen bei seinen Fehlern. Den Großteil der nächsten zwei Jahrzehnte ist diese eine Methode "der" Base64-Dekodierer in C#. - .NET 2.0, 2005. Die
Base64FormattingOptions-Enum trittConvertbei, bringt die MIME-Stil-Zeilenumbrüche zur Kodier-Seite (und die passende Weißraum-Toleranz zur Dekodier-Seite, wo sie bereits still und leise am Werk ist). - .NET Core 2.1, 2018. Das Span-Zeitalter.
Convertbekommt dieTry-Methoden und ein span-basiertes Kodieren, und die neueSystem.Buffers.Text.Base64-Klasse kommt mit ihremOperationStatus-Vertrag, In-Place-Dekodieren undIsValid, gebaut für die Null-Allokations-Welt des speicherfokussierten Neuschreibens. - .NET 5, 2020. Die Hex-Geschwister (
Convert.ToHexStringund Freunde) erscheinen, dasselbe Designmuster wie Base64, angewandt auf ein 16-symboliges Alphabet, ein Zeichen dafür, dass das Konversions-Klassen-Muster zu einem Hausstil geworden ist. - .NET 6, 2021.
X509Certificate2.CreateFromPemmacht PEM zu einer First-Class-Eingabe, und eine ganze Klasse an manuellem Rüstungs-Abstreifen-Code wird auf modernen Runtimes optional. - .NET 9, November 2024.
System.Buffers.Text.Base64Urllandet endlich an Bord nach Jahren von Community-Wünschen, und dasMicrosoft.Bcl.Memory-Paket portet es zurück auf .NET Framework 4.6.2 und höher für die Legacy-Codebasen, die immer noch alles betreiben. - .NET 11, zur Zeit des Schreibens in der Vorschau. Das nächste Release, für Ende 2026 erwartet, fügt den vorhandenen Typen weitere Base64-Bequemlichkeits-APIs und Overloads hinzu und setzt den langsamen Marsch zu einer ergonomischeren Oberfläche fort.
Lohnt sich im Hinterkopf zu behalten: Das Kodieren selbst ist weit älter als all das. Die erste standardisierte Verwendung dessen, was wir heute MIME-Base64 nennen, war das Privacy-Enhanced-Mail-Protokoll 1987 (RFC 989), MIME standardisierte die 76-Zeichen-umgewickelte Form 1993, und RFC 4648 gab dem Format 2006 seine moderne, alphabet-bewusste Spezifikation, einschließlich der URL-sicheren Variante. C# hat all das geerbt: Jede Umbruchs- und Padding-Eigenheit, die Sie in einem 30 Jahre alten E-Mail-Format treffen, ist eine Eigenheit, für die der C#-Dekodierer ausgelegt war.
Neugierige C#-Fakten
- Der kleinste Smoke-Test.
TWFudekodiert zuMan. Drei Bytes, kein Padding, keine Ausreden. Es ist das Hallo-Welt des Base64-Debuggens in C# und übt den kompletten Happy Path in vier Zeichen aus. - Ein Dekodierer mit Postgeschichte. Die Weißraum-Toleranz ist kein Zufall der Implementierung - es ist eine Designentscheidung, die von MIME geerbt wurde: Ein kompletter 76-Zeichen-umgewickelter E-Mail-Body, mit all seinen CRLF-Paaren, ist ein gültiges einzelnes Argument für
Convert.FromBase64String. Der Dekodierer wurde gebaut, um das Format zu fressen, das E-Mail seit dreißig Jahren verwendet. - Ein Fehler, drei Ursachen. Die klassische
FormatException-Meldung listet alle drei Fehlerszenarien auf, die sie möglicherweise berichtet (schlechtes Zeichen, zu viel Padding, fehlplatziertes Padding), und sagt nicht, welches ausgelöst wurde. Sie ist die einzige Fehlermeldung in der API-Oberfläche, die wie eine Multiple-Choice-Frage funktioniert. - Ein Namespace, der ein wenig lügt.
System.Buffers.Textklingt, als ginge es um Textverarbeitung, aber es ist tatsächlich das Zuhause der Binär-zu-Text-Konversion im Allgemeinen: DieUtf8ParserundUtf8Formatter, die Zahlen und Daten direkt nach UTF-8 parsen, wohnen gleich neben den Base64-Klassen. - Padding ist auf einer Seite der Familie optional. Die
Base64Url-Klasse dekodiertAQIDBA(sechs Zeichen, kein Padding) undAQIDBA==(dieselben Bytes mit Padding) zu denselben vier Bytes, während der klassische Dekodierer nur die gepaddete Form akzeptiert. Zwei Dekodierer, zwei Verträge, eine Runtime. - Strings, die nicht existieren sollten. Ein C#-String kann legal NUL-Bytes enthalten, also kann
Encoding.UTF8.GetStringüber dekodiertes Binäres einen "String" voller Steuerzeichen produzieren, den die Konsole, Ihr CSV-Writer und die Hälfte der JSON-Bibliotheken auf dem Planeten jeweils unterschiedlich behandeln werden. Das Typsystem erlaubt es; das Ökosystem größtenteils nicht. - Ein 1.1-Relikt in gutem Zustand.
Convert.FromBase64CharArrayhat seit April 2003 dieselbe Drei-Parameter-Signatur und hat die Generics-Revolution, die Span-Revolution und die URL-sichere Revolution überlebt, ohne dass auch nur ein einziges Overload hinzugekommen wäre. Das char-array-Zeitalter von C# ist nicht verschwunden; es ruht sich nur aus. - Elf Zeichen, acht Bytes. YouTubes Video-Identifikatoren sind base64url ohne Padding: 11 Zeichen, die zu 8 Bytes dekodieren.
Base64Url.GetMaxDecodedLength(11)sagt Ihnen die 8, und das Dekodieren ist ein Einzeiler, was eine schöne Art ist, den Tag zu beenden, wenn Sie die Art von Person sind, die solche Dinge schreibt.
Die andere Richtung
Das war die Dekodier-Seite, und dort lebt der größte Teil des Schmerzes, denn Dekodieren ist der Ort, an dem Sie auf die Daten anderer Leute treffen: ihre Padding-Entscheidungen, ihre Zeilenumbrüche, ihre Alphabete, ihre Tokens. Die entgegengesetzte Richtung, die eigenen Bytes zu nehmen und in Base64 zu packen, ist ein ruhigeres Problem mit seinem eigenen Set an Entscheidungen, die getroffen werden müssen, und seinem eigenen Set an Fallen. Base64-Kodierung in C#, von der 76-Zeichen-Frage bis zu URL-sicheren Tokens, wird im unten verlinkten Begleit-Artikel vertieft behandelt, und es ist ein kurzes, befriedigendes Lesen, wenn man weiß, wonach man suchen muss.
Zuletzt aktualisiert: 2026-09-08
Verwandter Artikel: Base64-Kodierung in C# (CSharp): Ein vollständiger Leitfaden