Base64-Dekodierung in PowerShell: Ein vollständiger Leitfaden
Irgendwo in einer Log-Zeile, in einer Konfigurationsdatei oder in einer Fehlermeldung stoßen Sie darauf: eine lange Kette aus Buchstaben und Ziffern mit dem gelegentlichen Plus oder Slash, und ein oder zwei Gleichheitszeichen, die verdächtig am Ende parken. Es sieht aus wie Rauschen. Ist es aber nicht. Es ist Base64, und Sie wissen schon, was Sie wollen: das, was es verbirgt.
Base64 ist eine Übersetzung, keine Kompression und kein Schloss. Es schreibt jede Folge von Bytes in druckbaren Text um, vier Zeichen pro drei Eingabe-Bytes (das kodierte Datenmaterial wird damit etwa 33% größer als das Original), und zwar mit einem Alphabet von 64 Zeichen plus dem Gleichheitszeichen als Padding am Ende. Die Startseite dieser Site geht das Alphabet, die Bit-Mathematik und die Varianten im Ganzen durch, also widmet sich dieser Artikel dem, wo PowerShell den Unterschied macht: der einen .NET-Methode, die Sie aufrufen, den Regeln, die sie durchsetzt, und die guten Dutzend Ecken der echten Arbeit, in denen das Dekodieren in PowerShell interessant wird.
Die Methode und ihr Vertrag
PowerShell liefert kein eigenes Base64-Cmdlet mit. Die Arbeit erledigt eine Methode auf einer .NET-Klasse, die seit .NET Framework 1.1 im Jahr 2003 Teil des Frameworks ist, drei Jahre bevor PowerShell selbst erschien:
$bytes = [System.Convert]::FromBase64String("SGVsbG8sIFdvcmxkIQ==")
[System.Text.Encoding]::UTF8.GetString($bytes)
# Hello, World!
Das ist die ganze API: ein String rein, ein Byte-Array raus. Es funktioniert in jedem PowerShell auf jedem Betriebssystem, in Windows PowerShell 5.1 und in PowerShell 7 auf Windows, Linux und macOS, weil es schlicht .NET ist. Der Vertrag ist kurz genug zum Auswendiglernen, also hier als Tabelle:
| Eingabe | Was Sie zurückbekommen |
|---|---|
$null |
Ein leeres Array, kein Fehler. PowerShell wandelt $null stillschweigend in einen leeren String um, bevor der Aufruf startet |
| Ein leerer String | Ein leeres Array, kein Fehler |
| Ein gültiger Payload | Ein byte[], nie ein String, auch wenn die Daten Text sind |
| Ein ungültiger Payload | Eine FormatException, eingepackt für Sie in eine MethodInvocationException |
Eine Warnung, bevor Sie Fehlerbehandlung schreiben: Diese FormatException hat eine einzige Meldung, die drei verschiedene Sünden abdeckt. Ein Zeichen außerhalb des Alphabets, mehr als zwei Padding-Zeichen oder ein Zeichen, das kein Leerraum ist und sich unter dem Padding versteckt, erzeugen alle genau denselben Satz. Wenn Sie sie sehen, verrät Ihnen die Meldung nicht, welche Sünde Sie begangen haben, also gehen Sie zurück und lesen Sie Ihre Eingabe:
try {
[System.Convert]::FromBase64String("SGV!G8s=")
}
catch {
$real = $_.Exception.InnerException
$real.GetType().Name
# FormatException
$real.Message
}
Und die Regel vom Vielfachen von vier hat eine Kante, die Leute beim ersten Mal überrascht. Vier Zeichen ohne jegliches Padding sind völlig gültig; es bedeutet nur, dass die übrig gebliebenen Bits im letzten Zeichen verworfen werden. Drei Zeichen sind kein Vielfaches von vier und werden abgelehnt:
[System.Convert]::FromBase64String("SGVs").Count
# 3: vier Zeichen ohne Padding sind in Ordnung
[System.Convert]::FromBase64String("SGV")
# FormatException: drei Zeichen sind kein Vielfaches von vier
Was der Decoder annimmt und was nicht
Der Decoder ist streng, was das Alphabet betrifft, und großzügig in genau einer Sache. Die gültigen Zeichen sind die 64 Base64-Ziffern (A bis Z, a bis z, 0 bis 9, Plus und Slash) und das Gleichheitszeichen als Padding am Ende. Genau vier Leerraumzeichen werden ignoriert, wo immer und so oft sie auch auftreten: der Tabulator, der Zeilenvorschub, der Wagenrücklauf und das Leerzeichen. Die offizielle .NET-Dokumentation führt sie unter ihren Unicode-Namen auf, und das zeigt, dass es eine dokumentierte Garantie ist und kein glücklicher Zufall.
In der Praxis ist das eine Superkraft. MIME, die Mail-Kodierung, die Base64 auf die Landkarte brachte, bricht kodierte Zeilen bei 76 Zeichen um, also kommt ein Payload, der durch eine E-Mail, ein Ticket oder eine Log-Datei gereist ist, normalerweise über viele Zeilen zerbrochen an. Der Decoder kümmert sich nicht darum. Fügen Sie ihn ein, so wie er ist:
$wrapped = "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4gQmFzZTY0IHRleHQg`r`n" +
"YXJyaXZlcyB3cmFwcGVkIGF0IHNldmVudHktc2l4IGNvbHVtbnMgaW4gbWFpbCwgc28gdGhlIGRl`r`n" +
"Y29kZXIgbXVzdCBub3QgY2FyZS4="
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($wrapped))
# The quick brown fox jumps over the lazy dog. Base64-Text kommt an
# im Mail bei 76 Spalten umgebrochen, also kann der Decoder das ignorieren.
Alles andere, was kein Alphabetzeichen ist, ist ein harter Stopp. Die häufigsten Übeltäter in freier Wildbahn sind das nicht-brechende Leerzeichen (der Liebling von Text, der aus Webseiten kopiert wurde) und die Byte-Order-Mark (BOM) (das unsichtbare Zeichen, das Sie verfolgt, wenn eine Datei mit der falschen Kodierung gelesen wurde). Beides ist aus Sicht dieser Methode kein Leerraum, also werfen beide:
try {
[System.Convert]::FromBase64String("SGVs`u{00A0}G8=")
}
catch {
$_.Exception.InnerException.GetType().Name
# FormatException
}
Die Strenge ist Absicht, keine Laune. RFC 4648, der Standard, der Base64 im Jahr 2006 kodifiziert hat, sagt, dass Implementierungen Zeichen außerhalb des Alphabets ablehnen müssen, es sei denn, das Protokoll erlaubt ausdrücklich Nachsicht, denn ein Decoder, der fremde Zeichen stillschweigend verschluckt, lässt sich in einen verdeckten Kanal verwandeln, um Daten an alles vorbei zu schmuggeln, was nur das Alphabet prüft. Der .NET-Decoder folgt der strengen Regel, und genau das möchten Sie in der Regel.
Ein Byte-Array ist kein String
Die Methode hält absichtlich am Byte-Array an. Was diese Bytes bedeuten, ist eine zweite Entscheidung, die nur Sie treffen können, und falsch zu raten ist der berühmteste Fehler in PowerShell-Base64-Arbeit. Die Standard-Vermutung, UTF-8, ist für fast alles im Internet richtig, und der Hin-und-Rückweg besteht aus zwei Aufrufen:
$bytes = [System.Convert]::FromBase64String("SGVsbG8sIFdvcmxkIQ==")
[System.Text.Encoding]::UTF8.GetString($bytes)
# Hello, World!
Die Kodierungen, zu denen Sie in der Praxis greifen, und was jede davon tut, wenn Sie falsch raten:
| Kodierung | Wann Sie sie verwenden | Wenn Sie falsch raten |
|---|---|---|
UTF8 |
Web-APIs, JSON, JWTs, alles Moderne. Die sichere Standardwahl | Latin-1- oder UTF-16-Text kommt als Mojibake zurück |
Unicode (UTF-16LE) |
Der Payload kam von Windows-Tooling, einem Registry-Wert oder einem .NET-String, der vor dem Versand kodiert wurde | Jedes Zeichen bekommt eine Lücke um sich herum, weil Sie ein Byte gelesen haben, wo zwei gemeint waren |
ASCII |
Klassische HTTP-Basic-Zugangsdaten und andere garantierte 7-Bit-Protokolle | Alles über dem Wert 127 wird zu einem Fragezeichen |
Latin1 |
Alte europäische Texte von vor UTF-8 | Mehrbaytige UTF-8-Folgen zerfallen in mehrere falsche Buchstaben |
Default |
Fast nie. Es ist die System-Codepage der Maschine | Ihr Skript verhält sich anders bei jeder Windows-Regions-Einstellung |
Der klassische Fehler ist UTF-8-Text, der als UTF-16 dekodiert wird. Die Bytes sind echt, die Methode ist zufrieden, und das Ergebnis ist trotzdem Müll:
# "SGk=" sind die UTF-8-Bytes von "Hi"
[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String("SGk="))
# Ein einzelnes unlesbares Zeichen: 2 Bytes UTF-8, gelesen als eine 2-Byte-UTF-16-Einheit
Die praktische Regel: Wenn der dekodierte Text so aussieht, als hätte jedes Zeichen eine unsichtbare Lücke um sich herum, oder als käme er aus einem anderen Alphabet, sind Sie genau eine Kodierung daneben. Fragen Sie, wo die Daten erzeugt wurden, und wenn Sie unsicher sind, vertrauen Sie UTF-8, aber prüfen Sie mit den eigenen Augen die ersten paar Zeichen. Und entscheiden Sie die Kodierung vor dem Dekodieren, nicht nachdem das Mojibake in Ihrem Log aufgetaucht ist.
base64url: Das Alphabet, das sich in URLs anständig benimmt
Einen Cousin von Base64 werden Sie in jedem API-Token, in jedem JWT und in jeder in eine URL eingebetteten Kennung antreffen, die Sie je anfassen. Das Plus und der Slash von Standard-Base64 sind in einer URL nur nach Prozent-Kodierung legal, und das Padding aus Gleichheitszeichen sieht aus wie ein Feldtrenner. Also hat RFC 4648 ein URL- und Dateinamens-sicheres Alphabet definiert: dieselben 64 Zeichen, nur dass Plus zum Bindestrich wird und Slash zum Unterstrich. Padding wird in der Regel ganz weggelassen, weil die Länge der Daten es unnötig macht. Der RFC legt großen Wert darauf, dass diese Variante base64url und nicht einfach "base64" genannt werden soll, und der Rest dieses Abschnitts hält sich daran.
.NET liefert tatsächlich eine dedizierte Klasse dafür mit, System.Buffers.Text.Base64Url, hinzugefügt in .NET 9 mit schnellen Kodier- und Dekodier-Methoden, die vollständig um ReadOnlySpan<T>-Parameter herum gebaut sind. Aktuelles PowerShell (7.4 und neuer, sobald es auf einer .NET-Version läuft, die die Klasse mitliefert) kann diese span-nehmenden Overloads tatsächlich heute direkt aufrufen, dank einer impliziten Array-/String-zu-Span-Umwandlung, die der Method-Binder jetzt durchführt, also funktioniert [System.Buffers.Text.Base64Url]::DecodeFromChars("--__AQI") ohne Zeremonie. Das war nicht immer so: Windows PowerShell 5.1 und ältere PowerShell-7.x-Releases konnten überhaupt nicht an Span-Parameter binden, und die Klasse existierte vor .NET 9 schlicht nicht, also braucht jedes Skript, das auf 5.1, einem älteren 7.x oder einem Host vor .NET 9 laufen muss, weiterhin die portable Version: die beiden Zeichen tauschen und das Padding wiederherstellen, bevor der Text an den Standard-Decoder übergeben wird. Das hinzuzufügende Padding ist, was auch immer die Länge zu einem Vielfachen von vier macht:
$token = "--__AQI" # base64url, kein Padding
$standard = $token.Replace("-", "+").Replace("_", "/")
$pad = 4 - ($standard.Length % 4)
if ($pad -eq 4) { $pad = 0 }
$standard = $standard.PadRight($standard.Length + $pad, "=")
$bytes = [System.Convert]::FromBase64String($standard)
$bytes -join ","
# 251,239,255,1,2
Zwei Fallen lauern in diesem kleinen Block. Erstens die Padding-Mathematik: Ein Payload, dessen Länge bereits ein Vielfaches von vier ist, braucht kein Padding, und die -eq 4-Wache ist es, die den Ausdruck ehrlich hält. Zweitens die Richtung: Wenn Sie nur dekodieren, fügen Sie Padding hinzu und tauschen; Sie entfernen niemals Padding von Standard-Base64-Eingabe, weil Standard-Decoder es dort erwarten. Wenn die Quelle ein JWT oder ein API-Token ist, wird es base64url ohne Padding sein, und das obige Rezept ist genau die Form, die Sie wollen.
Einen JWT öffnen, ohne die Schlüssel
Ein JSON Web Token ist drei base64url-Segmente, verbunden durch Punkte: Header, Payload, Signatur. Die ersten beiden sind schlichtes JSON, und Base64 ist keine Verschlüsselung, also kann jeder mit dem Token beide lesen. Das ist ein Feature, kein Mangel: Der Token ist so gebaut, dass man ihn inspiziert, und die Signatur ist es, die ihn unverfälschbar macht. PowerShell macht den Blick dazu zu einem Dreizeiler:
$jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
$parts = $jwt.Split(".")
function Decode-UrlSegment([string]$segment) {
$standard = $segment.Replace("-", "+").Replace("_", "/")
$pad = 4 - ($standard.Length % 4)
if ($pad -eq 4) { $pad = 0 }
$standard = $standard.PadRight($standard.Length + $pad, "=")
return [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($standard))
}
Decode-UrlSegment $parts[0] | ConvertFrom-Json | ConvertTo-Json -Compress
Decode-UrlSegment $parts[1] | ConvertFrom-Json
# name-Eigenschaft:
(Decode-UrlSegment $parts[1] | ConvertFrom-Json).name
# John Doe
Drei Dinge zum Merken. Das dritte Segment, die Signatur, ist ebenfalls base64url, aber es dekodiert zu binären Signatur-Bytes, nicht zu Text, also erwarten Sie dort kein hübsches JSON. Der Header sagt Ihnen normalerweise nur, welcher Algorithmus den Token signiert hat (HS256, RS256, ...), und ein Header, der none sagt, ist ein rotes Flag, keine Bequemlichkeit. Und die Payload zu lesen heißt nicht, ihr zu vertrauen: base64 lässt Sie die Claims sehen, nur die Signatur macht sie echt. Wenn Ihr Job es ist, Token zu akzeptieren, verifizieren Sie die Signatur mit dem Schlüssel des Herausgebers; wenn Ihr Job es ist, einen zu debuggen, ist der obige Code alles, was Sie brauchen.
Dateien, PEM und der lange Weg zu Bytes
Die häufigste Dateiform ist eine Textdatei, die das Base64 von etwas Größerem enthält: ein Backup-Blob, ein heruntergeladenes Binary, ein serialisiertes Objekt. Die Runde braucht vier Zeilen, und der moderne Weg, die Ausgabe zu lesen, ist ein echtes Byte-Array, keine Text-Vermutung:
$encoded = Get-Content -Path ./payload.b64 -Raw
$encoded = $encoded.Trim()
$bytes = [System.Convert]::FromBase64String($encoded)
[System.IO.File]::WriteAllBytes("./payload.bin", $bytes)
$bytes.Length
# wie viele Bytes der Text getragen hat
Das Zurücklesen des ursprünglichen Binärs ist genau dort, wo PowerShell 6 und neuer sich lohnen. Der -AsByteStream-Parameter liest rohe Bytes, und zusammen mit -Raw gibt er Ihnen auf einen Schlag ein echtes byte[]:
$bytes = Get-Content -Path ./photo.png -AsByteStream -Raw
$encoded = [System.Convert]::ToBase64String($bytes)
Set-Content -Path ./photo.b64 -Value $encoded -NoNewline
$bytes.Length
# ursprüngliche Größe, vor der 33-prozentigen Textsteuer
Lassen Sie -Raw weg, und Sie bekommen einen Stream einzelner Byte-Objekte (ein Object[], wenn eingefangen), was zur Inspektion gut ist, aber für die Übergabe an .NET-Methoden, die ein Array erwarten, falsch. Und Windows PowerShell 5.1 hat gar kein -AsByteStream, also ist auf 5.1 das verlässliche Lesen [System.IO.File]::ReadAllBytes(), das überall existiert.
PEM ist der gepanzerte Cousin, den Sie aus jedem Zertifikat und jedem privaten Schlüssel kennen: ein Standard-Base64-Körper, normalerweise bei 64 Zeichen umgebrochen, zwischen -----BEGIN ...- und -----END ...-Zeilen. Die Panzerung ist Text; der Körper ist der Payload. Panzerung ab, Zeilen verbinden, dekodieren:
$pem = Get-Content -Path ./certificate.pem -Raw
$body = ($pem -split "`n") | Where-Object { $_ -notmatch "^-----" } | ForEach-Object { $_.Trim() }
$der = [System.Convert]::FromBase64String(($body -join ""))
$der.Length
# die binäre DER-Größe des Zertifikats
Weil der Standard-Decoder Leerraum ohnehin ignoriert, ist das -join "" doppelte Sicherheit statt eigentliche Anforderung, aber das Skript explizit zu halten, was es entfernt, lässt es auf jeder Maschine und jeder Zeilenenden-Konvention gleich verhalten. Die andere Richtung, DER-Bytes in PEM einzupacken, ist einfach der Base64-Encoder plus zwei Zeilen Text, und der Kodierungs-Artikel auf der Schwester-Site zeigt den 64-spaltigen Umbruch im Ganzen.
Zertifikate und die Windows-Werkzeugkiste
Zertifikate sind die schwersten Base64-Bürger im täglichen Geschäft, und PowerShell kann die ganze Familie tragen. Eine PFX-Datei ist ein binäres Bündel aus Zertifikat plus privatem Schlüssel, und es ist das Format, das Sie am häufigsten als Base64-Text in Konfigurationsdateien und Deployment-Skripten vorfinden. Die Rückdekodierung in ein lebendes Zertifikat ist mit dem .NET-Typ ein Einzeiler, und es funktioniert plattformübergreifend in PowerShell 7:
$bytes = [System.Convert]::FromBase64String($pfxText)
$password = ConvertTo-SecureString "secret" -AsPlainText -Force
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($bytes, $password)
$cert.Subject
# CN=example.org
$cert.NotAfter
# wenn es aufhört, wahr zu sein
PowerShell 7 liefert auch Get-PfxCertificate mit, das eine PFX-Datei direkt von der Platte mit einem -Password-Parameter liest, also können Sie für Dateien auf der Platte das manuelle Dekodieren ganz überspringen. Ein nacktes Zertifikat (ohne Schlüssel) ist noch einfacher: Die DER-Bytes gehen direkt in denselben X509Certificate2-Typ, ohne irgendein Passwort.
Außerhalb der Sprache sind zwei native Werkzeuge zu kennen. Auf Windows decodiert certutil -decode infile.b64 outfile eine Base64-Datei mit Datei-rein/Datei-raus-Semantik (fügen Sie -f hinzu, um zu überschreiben), was es zur ersten Wahl für schnelle Fixes in einer normalen Eingabeaufforderung macht. Sein Bruder certutil -encode hat ein Flag, das sich zu merken lohnt: -unicodetext wandelt den Eingabetext in UTF-16 um, bevor er Base64-kodiert wird, und versteckt damit eine ganze Kodierungs-Entscheidung in einem einzigen Schalter. Auf Linux und macOS ist das klassische Werkzeug base64 -d, das eine Datei oder die Standard-Eingabe decodiert und Zeilenumbrüche standardmäßig überspringt; bei GNU coreutils fügen Sie -i hinzu, wenn der Payload auch Leerzeichen, Tabs oder CRLF aus Windows-Mails mit sich trägt.
Kommandos in einer Base64-Enveloppe
PowerShell hat seit Version 1.0 einen eingebauten Grund, Base64 zu sprechen: der -EncodedCommand-Parameter des Hosts selbst. Sie geben pwsh einen Base64-String, er decodiert die Bytes als UTF-16LE, und das Ergebnis wird als Kommando ausgeführt. Der offizielle Zweck, direkt aus der Dokumentation, ist es, Kommandos einzureichen, die komplexe Anführungszeichen oder geschweifte Klammern benötigen, ohne mit den Quoting-Regeln der äußeren Shell zu kämpfen:
$command = "Write-Host encoded-hello"
$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command))
# VwByAGkAdABlAC0ASABvAHMAdAAgAGUAbgBjAG8AZABlAGQALQBoAGUAbABsAG8A
pwsh -NoProfile -EncodedCommand $encoded
# encoded-hello
Lesen Sie diese zweite Zeile genau, denn hier stolpert jeder: der Payload muss UTF-16LE sein, das ist [System.Text.Encoding]::Unicode. Wenn Sie das Kommando stattdessen als UTF-8 kodieren, decodiert PowerShell es fröhlich als UTF-16LE und führt ein aus Mojibake bestehendes Kommando aus, und die Fehlermeldung, die es dabei produziert, ist ein perfektes Porträt des Fehlers:
$wrong = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($command))
pwsh -NoProfile -EncodedCommand $wrong
# Fehler: eine Mauer aus unleserlichen Zeichen, "Der Begriff ... wird nicht erkannt..."
Derselbe Mechanismus ist der Grund, warum Sicherheitsteams sich um Base64 in PowerShell kümmern. Ein langer, undurchsichtiger Token, der an -EncodedCommand übergeben wird, ist eine häufige Form für automatisiertes Tooling, und genau deshalb decodieren Endpunkt-Schutzprodukte diese Payloads, bevor sie ausgeführt werden: Nichts an Base64 versteckt das Kommando vor einem Decoder, es versteckt es nur vor einem Menschen, der eine Prozessliste liest. Wenn Sie für Ihre eigene Automatisierung kodierte Kommandos erzeugen, halten Sie das Quell-Kommando neben dem Token, denn der Token selbst wird sich um 3 Uhr nachts nicht erklären.
Dekodieren, wenn die Eingabe riesig ist
Für Alltagsgrößen ist der Ein-Methode-Ansatz der schnelle. Ein fünf-Megabyte-Binary wird zu einem String von etwa sechs Millionen neunhunderttausend Zeichen, und das Dekodieren dieses Strings dauert einstellige Millisekunden auf einer modernen Maschine. Der eigene Hinweis der .NET-Dokumentation lautet, dass FromBase64String so entworfen ist, einen einzigen String zu verarbeiten, der alle Daten enthält, was wahr ist und was auch bis zu sehr großen Grenzen in Ordnung ist, weil die Methode den String an Ort und Stelle bearbeitet, ohne nennenswerte zusätzliche Kopien.
Wenn der Payload größer ist, als Sie ihn gerne in einem einzigen String halten würden, oder als Stream ankommt (ein Download, ein Socket, ein riesiges Log), ist das dokumentierte Werkzeug System.Security.Cryptography.FromBase64Transform, eingepackt in einen CryptoStream: Sie füttern ihn mit Base64-Text und lesen dekodierte Bytes heraus, und in jedem Moment ist nur ein kleiner Puffer aktiv. Beachten Sie, dass TransformStream, der C#-Helfer dafür, eine Erweiterungsmethode ist, und PowerShell sieht keine Erweiterungsmethoden, also instanziieren Sie den CryptoStream direkt:
$inputStream = [System.IO.File]::OpenRead("./payload.b64")
$transform = [System.Security.Cryptography.FromBase64Transform]::new()
$stream = [System.Security.Cryptography.CryptoStream]::new(
$inputStream, $transform, [System.Security.Cryptography.CryptoStreamMode]::Read)
$destination = [System.IO.File]::Create("./payload.bin")
$buffer = New-Object byte[] 65536
while (($read = $stream.Read($buffer, 0, $buffer.Length)) -gt 0) {
$destination.Write($buffer, 0, $read)
}
$destination.Dispose()
$stream.Dispose()
$inputStream.Dispose()
Für die neunzig Prozent der Jobs ist der einfache Weg immer noch der richtige: die gesamte Textdatei mit Get-Content -Raw lesen, sie trimmen, sie dekodieren, die Bytes schreiben. Greifen Sie zur Stream-Version, wenn die Datei zu groß ist, um sie bequem im Speicher zu halten, oder wenn die Daten stückweise ankommen. Und versuchen Sie nicht, über die Zeilen zu iterieren und jede Zeile separat zu dekodieren: Base64-Gruppen von vier Zeichen respektieren Ihre Zeilenumbrüche nicht, also decodiert eine Zeile, die eine Gruppe in der Mitte teilt, nicht für sich allein. Lesen Sie den gesamten Text, dann dekodieren Sie einmal.
Fallen, die Nachmittage kosten
- Die Charset-Vermutung. UTF-8 als UTF-16 gelesen, oder Latin-1 als UTF-8, produziert selbstbewusstes Mojibake. Entscheiden Sie die Kodierung aus der Herkunft der Daten, gehen Sie standardmäßig zu UTF-8, und sehen Sie sich die ersten paar dekodierten Zeichen an, bevor Sie dem Rest vertrauen.
- Unsichtbare Zeichen vom Web. Ein nicht-brechendes Leerzeichen oder eine Byte-Order-Mark (BOM), kopiert aus einer Seite oder einer Rich-Text-E-Mail, ist für den Decoder ein fremdes Zeichen und wirft die generische
FormatException. Schicken Sie die Eingabe durch.Trim()und eine Prüfung auf nicht-druckbare Zeichen, bevor Sie dekodieren. - Padding-Verwirrung. Standard-Base64 kommt mit
=oder==am Ende; base64url aus Tokens kommt ohne. Eines in das Rezept zu füttern, das für das andere gebaut ist, ist der häufigste stille Defekt in API-Arbeit, und der Längen-Check im base64url-Abschnitt ist der Guard. - Die eine Meldung, drei Verbrechen. Weil die
FormatException-Meldung schlechte Zeichen, übermäßiges Padding und schmutziges Padding alle auf einmal abdeckt, schicken Catch-Blöcke, die nur die Meldung loggen, Sie im Kreis herum. Loggen Sie auch die Länge der Eingabe und den ersten problematischen Bereich. - Ein String als Ergebnis erwartet. Das Ergebnis ist immer ein Byte-Array. Im Moment, in dem Sie es direkt wie einen String formatieren, bekommen Sie eine Liste von Zahlen, keinen Text. Konvertieren Sie mit einer expliziten Kodierung, einmal, ganz am Ende.
- Der 5.1-Datei-Standard. Windows PowerShell 5.1 liest Dateien ohne BOM mit der ANSI-Codepage des Systems, während PowerShell 7 UTF-8 annimmt. Wenn Ihr Skript die Base64-Textdatei auf 5.1 liest und die Datei UTF-8 ist und um den Payload herum Nicht-ASCII enthält, passiert die Beschädigung, bevor der Decoder sie überhaupt sieht.
- Base64 als Schloss behandeln. Es ist eine Übersetzung. Ein Passwort, Token oder Geheimnis in Base64 ist Klartext in einem Kostüm, und jeder Decoder auf dem Planeten, einschließlich dieses Artikels, öffnet es in einer Zeile.
Gewohnheiten, die Skripte ehrlich halten
- Externe Eingabe vor dem Dekodieren trimmen. Ein einzelnes
.Trim()beseitigt mehr Produktionsunfälle als jeder Fehler-Handler. - Validieren, bevor Sie dekodieren, wenn die Quelle nicht vertrauenswürdig ist: Nach dem Entfernen der vier erlaubten Leerraumzeichen sollte der String nur Alphabetzeichen mit höchstens zwei Gleichheitszeichen am Ende enthalten. Ein schneller Regex-Check verwandelt eine Mysterium-Ausnahme in eine saubere Meldung über abgelehnte Eingabe.
- Die Bytes als Bytes behalten, bis zum allerletzten Schritt. Einmal dekodieren, das
byte[]der Date-API oder dem Encoder übergeben, der es braucht, und erst dann mit einer bewussten Kodierung in Text konvertieren. - Längen loggen, keine Payloads. Die Größe der Eingabe und die Größe der dekodierten Ausgabe sagen Ihnen fast alles über einen Dekodier-Fehler, ohne dass Sie möglicherweise sensible Daten in das Log kleben.
- Für alles, was einen Draht überquert, notieren Sie, in welchem Alphabet es ist, standard oder base64url, und welche Padding-Konvention, in derselben Codezeile, die es dekodiert. Ihr zukünftiges Ich ist der Konsument dieses Hinweises.
Wie PowerShell seinen Decoder geerbt hat
Die kürzeste wahre Geschichte von Base64 in PowerShell ist, dass PowerShell nie einen geschrieben hat. Die Methode, die Sie verwenden, Convert.FromBase64String, erschien mit .NET Framework 1.1 im Jahr 2003, und jedes PowerShell seit Version 1.0 im November 2006 hat schlicht das .NET, auf dem es läuft, ausgelegt. Das Projekt hieß während der Entwicklung Monad, wurde zum ersten Mal öffentlich auf der Professional Developers Conference im Oktober 2003 gezeigt, und zur Zeit der Veröffentlichung war das .NET-Encoder-Decoder-Paar, das es einpackt, bereits drei Jahre alt und im täglichen Gebrauch.
Das Format selbst wurde im selben Jahr standardisiert, in dem die Shell startete. RFC 4648, veröffentlicht im Oktober 2006, ist das Dokument, das das Alphabet, die Padding-Regeln, die Erwartung eines strengen Dekodierens und die base64url-Variante festlegte, und es beschreibt immer noch exakt das Verhalten, das FromBase64String heute implementiert. Als PowerShell im August 2016 als PowerShell Core Open Source und plattformübergreifend wurde, kam der Decoder auf Linux und macOS ohne Änderungen mit an Bord, weil es nichts zu ändern gab.
Der eine echte Zusatz ist das von der Community gepflegte Microsoft.PowerShell.TextUtility-Modul aus der PowerShell Gallery, dessen ConvertFrom-Base64-Cmdlet dieselbe .NET-Methode einpackt und einen -AsByteArray-Schalter plus einen Text-Standard hinzufügt, der als UTF-8 dekodiert. Installieren Sie es mit Install-Module -Name Microsoft.PowerShell.TextUtility, wenn Sie die Cmdlet-Form bevorzugen, aber eine Warnung: Das Modul ist jetzt archiviert und wird nicht mehr aktiv gepflegt, was ein weiterer Grund ist, warum die eingebaute Methode für neue Skripte die Empfehlung bleibt.
Fakten, die sich zu merken lohnen
- Der Decoder ignoriert Tabs, Zeilenvorschübe, Wagenrückläufe und Leerzeichen überall in der Eingabe. Hundert umgebrochene Zeilen dekodieren genauso wie eine lange Zeile.
$nullund der leere String dekodieren beide zu einem leeren Array, ohne Klagen, wasFromBase64Stringan der Kante ungewöhnlich nachsichtig macht.- Die einzige
FormatException-Meldung deckt drei verschiedene Fehlermodi ab. Wenn sie abgefeuert wird, ist die Eingabe, nicht die Meldung, der Ort, an dem die Antwort sitzt. "SABpAA=="ist der StringHiin der eigenen internen Kodierung von PowerShell, UTF-16LE. Er ist doppelt so lang wie die UTF-8-Kodierung derselben beiden Buchstaben, und dieses Verhältnis ist der Fingerabdruck von Windows-nativem Text in jedem Base64, das Sie lesen werden.-EncodedCommandexistiert seit der ersten PowerShell-Veröffentlichung, und sein Payload ist vorgeschrieben als UTF-16LE, nicht UTF-8. Kodieren mit der falschen Kodierung, und die Shell führt Ihr Mojibake fröhlich aus.- Die neueren span-basierten Base64-Helfer von .NET, einschließlich der
Base64Url-Klasse, waren aus älteren PowerShell-Releases unerreichbar, weil Spans byref-ähnliche Typen sind, an die der Method-Binder nicht binden konnte. Das hat sich geändert: Aktuelles PowerShell (7.4+, auf einer .NET-Version, die neu genug ist, um die Klasse mitzuliefern) löst ein Array- oder String-Argument gegen einenReadOnlySpan<T>-Parameter auf, ohne zu murren, also funktioniert der direkte Aufruf heute. Der Zwei-Zeichen-Tausch verdient sich seinen Lohn als die Version, die auch auf Windows PowerShell 5.1 und älteren Hosts läuft, nicht als der letzte verbliebene Weg. Get-Content -AsByteStreamohne-Rawgibt Ihnen einen Stream von Byte-Objekten, kein Byte-Array. Fügen Sie-Rawhinzu, und der Typ ist genau das, was die .NET-Methoden erwarten.
Der lange Weg zurück
Alles in diesem Artikel dreht sich darum, einen Base64-String zu nehmen und die eigenen Daten zurückzubekommen. Die Spiegeloperation, Daten in Base64 zu verwandeln, sieht aus wie ein Einzeiler, bis man die Tatsache trifft, dass PowerShell-Strings keine Bytes sind, dass UTF-16 die Größe verdoppelt, dass der Zeilenumbruch zwei konventionelle Breiten hat und dass base64url-Ausgabe ihre eigene Zwei-Zeichen-Operation braucht. Diese Richtung bekommt ihre eigene ausführliche Behandlung, mit ihren eigenen Fallen und ihrer eigenen Geschichte, im verwandten Artikel auf der Schwester-Site, Base64-Kodierung in PowerShell, auf den diese Seite unten verlinkt.
Zuletzt aktualisiert: 2026-09-07
Verwandter Artikel: Base64-Kodierung in PowerShell: Ein vollständiger Leitfaden