Base64-Kodierung in PowerShell: Ein vollständiger Leitfaden
Sie haben einen String, eine Datei, ein Zertifikat oder ein Token, und die andere Seite der Leitung will es als eine lange Kette aus Buchstaben und Ziffern: druckbar, einfügbar in eine E-Mail, eine URL oder eine Konfigurationsdatei, ohne ein einziges binäres Byte, das den Transport kaputt macht. Das ist Base64. Es ist eine Übersetzung, keine Kompression und kein Schloss: drei Bytes Input werden zu vier Zeichen Output, daher wird der Text, den Sie verschicken, etwa 33 Prozent größer als das, was ihn gestartet hat, mit einem Alphabet aus 64 Zeichen plus dem Gleichheitszeichen als Padding am Ende.
Die Startseite dieser Site deckt das Alphabet, die Bit-Mathematik und die Varianten im Detail ab. Dieser Artikel deckt die Kodierungsrichtung von der PowerShell-Seite ab: die eine .NET-Methode, die Sie aufrufen werden, der fehlende Schritt, der jeden beim ersten Skript ausbremst, die Umbruch-Konventionen, die je nach Protokoll unterscheiden, das URL-sichere Alphabet und die Handvoll echter Jobs, bei denen die Kodierung in PowerShell die Sorgfältigen belohnt und die Nachlässigen bestraft.
Die Methode und der fehlende Schritt
PowerShell bringt kein eigenes Base64-Cmdlet mit. Die Arbeit erledigt eine Methode, die seit .NET Framework 1.1 im Jahr 2003 Teil des .NET-Frameworks ist, drei Jahre, bevor PowerShell selbst erschien:
$bytes = [System.Text.Encoding]::UTF8.GetBytes("Hello")
[System.Convert]::ToBase64String($bytes)
# SGVsbG8=
Das ist die gesamte API: ein Byte-Array rein, ein String raus, in jedem PowerShell auf jedem Betriebssystem, denn es ist schlicht .NET. Der fehlende Schritt ist die erste Zeile des Beispiels, und dort verlieren Anfänger ihre erste Stunde. Die Methode akzeptiert nicht Ihren String. Sie akzeptiert Bytes, und die Frage "welche Bytes bedeutet mein String" ist eine Kodierungsfrage, die nur Sie beantworten können. Hier ist der Vertrag der Overloads, die Sie tatsächlich aus PowerShell aufrufen können:
| Was Sie übergeben | Was Sie bekommen |
|---|---|
byte[] |
Eine lange Zeile Standard-Base64, mit =-Padding, wo es die Länge verlangt |
byte[] plus InsertLineBreaks |
Dasselbe Datenmaterial, umgebrochen bei 76 Zeichen mit CRLF zwischen den Zeilen |
byte[], offset, count |
Nur die angeforderte Scheibe des Arrays, kodiert |
Ein String wie "Hello" |
Eine Konversions-Exception. PowerShell kann einen String nicht von allein in ein Byte-Array verwandeln |
$null |
Eine ArgumentNullException, für Sie verpackt in einer MethodInvocationException |
Achten Sie darauf, was in dieser Tabelle fehlt: Es gibt keinen Overload, der "kodiere diesen Text" sagt. Text in PowerShell zu kodieren ist immer ein zweistufiger Prozess. Sie entscheiden die Kodierung, Sie erzeugen die Bytes, und erst dann tritt die Base64-Methode ins Gespräch ein. Halten Sie diese beiden Entscheidungen im Skript sichtbar getrennt, denn die zweite ist unsichtbar, und die erste ist es, wo die Bugs leben.
Text kodieren: Erst die Kodierung wählen
Der sichere Standard für alles, was das moderne Internet überquert, ist UTF-8. Web-APIs, JSON, JWTs, alles, was ein Browser oder ein Server im letzten Jahrzehnt geschrieben hat, erwartet unter dem Base64 UTF-8-Bytes, und das zweistufige Muster ist die Gewohnheit, die Sie aufbauen wollen:
$text = "Hello, PowerShell!"
$bytes = [System.Text.Encoding]::UTF8.GetBytes($text)
$encoded = [System.Convert]::ToBase64String($bytes)
# SGVsbG8sIFBvd2VyU2hlbGwh
Wenn Sie zu einer anderen Kodierung greifen, bedienen Sie in der Regel ein Legacy-System, und die Tabelle unten ist der praktische Leitfaden:
| Kodierung | Nehmen Sie sie, wenn | Wenn Sie die falsche wählen |
|---|---|---|
UTF8 |
Web-APIs, JSON, JWTs, alles Moderne. Die Standardwahl | Der Dekodierer auf der anderen Seite sieht Mojibake statt Ihrem Text |
Unicode (UTF-16LE) |
Der Konsument ist eine Windows- oder .NET-Komponente, die .NET-Strings kodiert, oder -EncodedCommand |
Ihr Payload ist doppelt so lang, wie der Konsument erwartet, und voller Überraschungen |
ASCII |
Klassische 7-Bit-Protokolle wie HTTP-Basic-Anmeldedaten | Alles über dem Wert 127 wird ersetzt, bevor die Kodierung überhaupt stattfindet |
Latin1 |
Legacy-europäische Systeme, die älter sind als UTF-8 | Ein Byte pro Zeichen, und jedes Nicht-Latin-1-Zeichen wird zu einem Fragezeichen |
Ein nützlicher Debugging-Trick funktioniert in beide Richtungen: Das Padding und die Länge des Base64 verraten Ihnen, wie viele Bytes kodiert wurden, und die Art, wie der dekodierte Text aussieht, verrät Ihnen, aus welcher Zwei-Byte- oder Ein-Byte-Welt er stammt. Ein Payload, dessen Größe verdächtig gerade ist und der voller abwechselnder normaler und leeraussehender Zeichen ist, ist normalerweise UTF-16 in einem UTF-8-Kostüm, oder umgekehrt.
Die UTF-16-Überraschung
PowerShell speichert Strings intern als UTF-16, und diese Tatsache sickert an einem bestimmten, sehr häufigen Platz in die Base64-Arbeit ein: Sie schreiben das Base64 für einen Konsumenten, der selbst eine .NET- oder Windows-Komponente ist, und Sie kodieren mit Unicode, denn so sind .NET-Strings. Für manche Konsumenten ist das ein korrekter Instinkt, für alle anderen ein Fehler, der die Größe verdoppelt. Dieselben vier sichtbaren Zeichen, zwei Kodierungen:
$same = "Café"
[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($same))
# Q2Fmw6k= fünf Bytes
[System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($same))
# QwBhAGYA6QA= acht Bytes
Derselbe Text, doppelte Größe, und die beiden Strings sind nicht austauschbar: Ein Konsument, der das eine erwartet und das andere erhält, schlägt nicht laut fehl; er liest einfach Müll. Die Regel, die verhindert, dass Ihnen das beißt, ist, die Kodierung als Teil des Protokolls zu behandeln und nicht als lokale Detailfrage. Wenn das empfangende System ein Browser, eine REST-API oder ein moderner Server ist, ist es UTF-8, es sei denn, die Dokumentation sagt etwas anderes. Wenn es der PowerShell-Host selbst über -EncodedCommand ist, oder ein .NET-String in einer nur unter Windows laufenden Pipeline, ist es UTF-16LE. Wenn das Protokoll nichts sagt, fragen Sie die andere Seite, womit sie GetString aufrufen werden, denn das ist die Frage, die tatsächlich entscheidet.
Zahlen, Bytes und alles andere
Die Methode ist deklariert, ein Byte-Array zu nehmen, aber die Typumwandlung von PowerShell ist großzügig darüber, was als eines zählt, und die Kanten zu kennen bewahrt Sie vor Überraschungen:
[System.Convert]::ToBase64String([byte[]](1, 2, 3, 250, 251))
# AQID+vs=
[System.Convert]::ToBase64String([char[]]"Café")
# Q2Fm6Q== ein Byte pro Zeichen, der Zeichenwert als Zahl
[System.Convert]::ToBase64String([int[]](72, 101, 108, 108, 111))
# SGVsbG8=
[System.Convert]::ToBase64String(123)
# ew== eine einzelne Zahl wird akzeptiert, wo ein ganzes Array erwartet wird
Zwei Kanten in dem Block verdienen Aufmerksamkeit. Zeichen-Arrays konvertieren ein Byte pro Zeichen, wobei der Zahlenwert des Zeichens verwendet wird, was für lateinischen Text genau das ist, was die Legacy-Systeme, die diesen Trick nutzen, erwarten, und für alles darüber hinaus still und leise die falschen Bytes produziert. Eine ganze Zahl größer als 255 ist die Kante, die stattdessen laut fehlschlägt: Die Byte-Umwandlung von PowerShell verweigert Werte außerhalb von 0-255 mit einer Exception, daher stoppt 256 das Skript beim Cast, statt Ihre Daten still und leise zu beschädigen. Wenn Ihre Quelle Zahlen sind, machen Sie den Cast explizit: [byte[]](1, 2, 3) sagt genau, was es meint.
Übergben Sie der Methode einen String, und Sie bekommen dieselbe laute Behandlung aus einem anderen Grund: Es gibt keinen Weg zu wissen, welche Bytes ein String bedeutet, daher gibt die Umwandlungs-Engine von PowerShell auf. Übergben Sie $null, und .NET wirft, bevor es irgendetwas tut. Beides ist korrektes Verhalten, und beides ist der Grund, warum das zweistufige Muster aus dem ersten Abschnitt das einzige Muster ist, das sich zu haben lohnt.
Zeilen-Umbruch: 76, 64 und keiner
Standardmäßig produziert der Encoder eine lange Zeile, egal wie viel Daten Sie ihm geben. Für eine Datei von einigen Kilobytes ist das in Ordnung. Für Daten, die von einem Menschen gelesen, in eine E-Mail eingefügt oder in einem Source-Control-Diff verglichen werden, ist eine Wand aus acht Millionen Zeichen ein praktisches Problem, und die Konvention ist, umzubrechen. PowerShell und die Protokolle, die es bedient, kennen drei Breiten, und sie sind nicht austauschbar:
| Breite | Wer erwartet sie | Zeilenende |
|---|---|---|
| 76 Zeichen | MIME, Mail und die meisten Text-Transporte. Der Standard von InsertLineBreaks |
CRLF |
| 64 Zeichen | PEM-Dateien: Zertifikate, private Schlüssel und der Rest der -----BEGIN-Familie |
Konventionell LF |
| Keine | APIs, Tokens, Konfigurationsdateien, alles, wo der Payload maschinell verarbeitet wird | Keines |
Der eingebaute Umbruch ist eine Ein-Parameter-Änderung, und er ist der, den Sie für mail-artige Payloads wollen:
$text = "The quick brown fox jumps over the lazy dog. Base64 output arrives wrapped at different widths depending on who is reading it."
$wrapped = [System.Convert]::ToBase64String(
[System.Text.Encoding]::UTF8.GetBytes($text),
[Base64FormattingOptions]::InsertLineBreaks)
# 76 Zeichen pro Zeile, CRLF dazwischen, genau wie MIME es erwartet
PEM ist die Ausnahme vom Eingebauten, denn OpenSSL und das gesamte -----BEGIN-Ökosystem brechen bei 64 Zeichen um, und kein .NET-Flag erzeugt diese Breite. Die Schleife ist kurz, und sie ist das Standardrezept:
$der = [System.IO.File]::ReadAllBytes("./certificate.der")
$b64 = [System.Convert]::ToBase64String($der)
$lines = for ($i = 0; $i -lt $b64.Length; $i += 64) {
$b64.Substring($i, [Math]::Min(64, $b64.Length - $i))
}
$pem = @("-----BEGIN CERTIFICATE-----") + @($lines) + @("-----END CERTIFICATE-----")
Set-Content -Path "./certificate.pem" -Value ($pem -join "`n")
Der Grund, warum die Breite überhaupt eine Rolle spielt, ist, dass Base64-Gruppen von vier Zeichen sich nicht um Zeilenumbrüche kümmern, daher kann ein Dekodierer die Umbrüche komplett ignorieren oder sie durchsetzen. Der Dekodierer, den diese Site verwendet, ignoriert sie, aber strenge Konsumenten, und davon gibt es viele in der Zertifikats- und Mail-Welt, behandeln einen unerwarteten Umbruch als fremdes Zeichen und lehnen den Payload ab. Wenn Sie eine Breite wählen, schließen Sie einen Vertrag mit dem Konsumenten, und ein Kommentar im Skript, der benennt, mit wem Sie den Vertrag schließen, lohnt sich.
base64url: Zwei Zeichen und eine Padding-Entscheidung
Das Plus und der Schrägstrich des Standard-Base64 sind innerhalb einer URL nur nach dem Percent-Encoding legal, und das Equals-Padding liest sich wie ein Feldtrenner. Also hat RFC 4648 ein URL- und dateinamensicheres Alphabet definiert: dieselben 64 Zeichen, nur wird Plus zum Bindestrich und Schrägstrich zum Unterstrich, und das Padding wird in der Regel weggelassen, denn die Länge der Daten macht es überflüssig. Jedes API-Token und jeder JWT, mit dem Sie je zu tun hatten, sind in dieser Variante geschrieben, und der Standard besteht darauf, dass sie base64url und nicht einfach base64 genannt wird.
Der Standard-Encoder von PowerShell produziert das Standard-Alphabet, daher ist die Umwandlung nach base64url zwei Zeichen-Tausche und eine Padding-Entscheidung:
$bytes = [System.Text.Encoding]::UTF8.GetBytes("Париж encoded 大阪")
$standard = [System.Convert]::ToBase64String($bytes)
$standard
# 0J/QsNGA0LjQtiBlbmNvZGVkIOWkp+mYqg== das Standard-Alphabet, Padding inklusive
$url = $standard.Replace("+", "-").Replace("/", "_").TrimEnd("=")
$url
# 0J_QsNGA0LjQtiBlbmNvZGVkIOWkp-mYqg die URL-sichere Form, Padding entfernt
Das Entfernen des Paddings ist in der base64url-Welt sicher, denn der Konsument berechnet aus der Länge des Strings neu, wie das Padding ausgesehen hätte. Das stimmt aber nicht überall, daher treffen Sie die Entscheidung explizit: Padding weg lassen für Tokens, JWT-Segmente und URL-Einbettung, beibehalten für alles, was einen strengen Standard-Alphabet-Konsumenten füttert, und notieren Sie, was Sie gewählt haben. Die .NET-Runtime liefert tatsächlich eine dedizierte Klasse für dieses Alphabet mit, System.Buffers.Text.Base64Url (hinzugefügt in .NET 9), mit Methoden, die auf ReadOnlySpan<T>-Parametern aufbauen. Das aktuelle PowerShell (7.4 und neuer, sobald es auf einer .NET-Version läuft, die die Klasse mitbringt) kann diese tatsächlich direkt aufrufen - [System.Buffers.Text.Base64Url]::EncodeToString($bytes) funktioniert heute, denn der Method-Binder wandelt ein Array-Argument jetzt implizit in einen Span um - aber der Tausch der zwei Zeichen ist weiterhin der, zu dem Sie greifen, wann immer das Skript auf Windows PowerShell 5.1, einem älteren PowerShell-7.x-Release oder einem Host auf einer Runtime vor .NET 9 laufen muss, und er funktioniert in jeder dieser Versionen.
Einen JWT prägen
Ein JSON Web Token ist der Flaggschiff-Fall der Praxis für base64url, und er ist auch ein guter Kompletttest der Kodier-Pipeline, denn ein JWT besteht aus drei kodierten Segmenten, verbunden durch Punkte: der Header, der Payload und die Signatur. Die ersten beiden sind kompaktes JSON in base64url, und das dritte ist die binäre Ausgabe eines Hashes über den exakten Text der ersten beiden. Hier ist ein komplettes HS256-Token, in PowerShell gebaut, von Anfang bis Ende:
$header = @{ alg = "HS256"; typ = "JWT" } | ConvertTo-Json -Compress
$payload = @{ sub = "1234567890"; name = "John Doe"; iat = 1516239022 } | ConvertTo-Json -Compress
function UrlEncode64([byte[]]$bytes) {
$standard = [System.Convert]::ToBase64String($bytes).TrimEnd("=")
return $standard.Replace("+", "-").Replace("/", "_")
}
$left = (UrlEncode64 ([System.Text.Encoding]::UTF8.GetBytes($header))) + "." + (UrlEncode64 ([System.Text.Encoding]::UTF8.GetBytes($payload)))
$hmac = [System.Security.Cryptography.HMACSHA256]::new([System.Text.Encoding]::UTF8.GetBytes("secret"))
$signature = UrlEncode64 ($hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($left)))
$jwt = $left + "." + $signature
# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MTYyMzkwMjIsInN1YiI6IjEyMzQ1Njc4OTAiLCJuYW1lIjoiSm9obiBEb2UifQ.6MWZy9doHbfyomJd4soTRUQft7PmRM2EyxxT3SLoiyE
# auf PowerShell 7.4; die innere Schlüsselreihenfolge der Segmente - und damit die Signatur - kann je nach Version abweichen
Schauen Sie auf das Signatur-Segment: eine Abfolge des base64url-Alphabets, das, in dem Plus als Bindestrich und Schrägstrich als Unterstrich erscheint. Drei Dinge an diesem Beispiel werden Sie vor Produktionsvorfällen bewahren. Erstens wird die Signatur über den exakten JSON-Text berechnet, einschließlich seiner Schlüsselreihenfolge und seiner Abstände, daher muss das JSON, das Sie signieren, und das JSON, gegen das Sie prüfen, byte für byte dasselbe sein. Das ConvertTo-Json von PowerShell entscheidet die Schlüsselreihenfolge für Sie, und das ist nichts, das Sie kontrollieren, daher sortieren Sie die Segmente eines Tokens nicht von Hand neu oder formatieren sie zwischen Signieren und Prüfen um. Zweitens ist -Compress keine Kosmetik: Ein Token, dessen Header oder Payload ein einziges Leerzeichen enthält, ist ein Token, das sich gegen eine konforme Implementation nie verifizieren wird, denn die Standardform ist kompakt. Drittens ist der Zeitstempel iat Sekunden seit der Unix-Epoche, und ein Payload, das aus Get-Date gebaut wurde, ohne umzuwandeln, wird jahrelang außerhalb des Bereichs liegen. Die Dekodier-Richtung, der Blick in ein Token, das jemand anderes geprägt hat, ist im verwandten Artikel auf der Schwestersite abgedeckt.
Dateien und der Byte-Stream
Dateien sind der häufigste Payload überhaupt, und die Pipeline ist kurz. Die Datei als Bytes lesen, kodieren, Text schreiben. Die zwei Zeilen, die zählen, sind das Lesen, das ein Byte-Lesen sein muss, und das Schreiben, das normalerweise keinen abschließenden Zeilenumbruch hinzufügen darf:
$bytes = [System.IO.File]::ReadAllBytes("./photo.png")
$encoded = [System.Convert]::ToBase64String($bytes)
Set-Content -Path "./photo.b64" -Value $encoded -NoNewline
$encoded.Length
# die Textgröße, die Sie gleich verschicken
Zwei praktische Hinweise. Der erste ist Arithmetik: Base64 macht alles größer, und für eine 10-Megabyte-Datei ist der Text, den Sie verschicken, etwa 13,4 Megabyte. Wenn der Transport eine Größenbeschränkung hat, oder wenn dieser Text in einen E-Mail-Body oder eine URL soll, rechnen Sie vorher, bevor Sie kodieren, nicht erst nach dem Fehler. Der zweite ist der abschließende Zeilenumbruch: Set-Content fügt standardmäßig einen hinzu, und während der Dekodierer, den diese Site verwendet, und die meisten modernen Dekodierer ihn ignorieren, tun es manche strengen Konsumenten nicht. -NoNewline kostet Sie nichts und nimmt die Frage weg.
PowerShell 6 und neuer bieten ein zweites Lesen, das in der Sprache bleibt: Get-Content -AsByteStream -Raw gibt die Datei als ein einzelnes Byte-Array in einem Aufruf zurück, was eine ordentliche Alternative zum .NET-ReadAllBytes ist und sich für diesen Zweck identisch verhält. Auf Windows PowerShell 5.1, das kein -AsByteStream hat, ist das .NET-Lesen die einzige Option, und es ist das, das sich auf jeder Version der Shell gleich verhält.
Zertifikate: Von PEM und PFX zu Text
Zertifikate sind die schwersten Kodierungs-Bürger im laufenden Betrieb, denn Deployment-Prozesse lieben es, sie als Text mitzuführen. Ein PEM-Zertifikat ist ein umgebrochener Base64-Body zwischen Panzerungszeilen, und das Rezept aus dem Umbruch-Abschnitt ist der komplette Export:
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new([System.IO.File]::ReadAllBytes("./certificate.der"))
$cert.Subject
# CN=example.org
$b64 = [System.Convert]::ToBase64String($cert.RawData)
# eine lange Zeile der binären Form des Zertifikats
Das PFX-Format ist das andere Arbeitstier: eine einzige binäre Datei, die das Zertifikat zusammen mit seinem privaten Schlüssel hält, und genau deshalb ist es das Format, das Sie am häufigsten als Base64-Text in Deployment-Skripten und Konfigurations-Speichern herumliegen sehen. Eines zu kodieren ist die einfache Datei-Pipeline aus dem vorherigen Abschnitt, und die Leserichtung in PowerShell 7 ist eine Ein-Cmdlet-Sache:
$pfxBytes = [System.IO.File]::ReadAllBytes("./certificate.pfx")
$pfxB64 = [System.Convert]::ToBase64String($pfxBytes)
# die Textform des Bündels, bereit für eine Konfigurationsdatei
Get-PfxCertificate -FilePath "./certificate.pfx" -Password (ConvertTo-SecureString "secret" -AsPlainText -Force)
# das lebende Zertifikat, kein manuelles Dekodieren nötig
Ein Satz Sicherheit, klar ausgesprochen, denn Base64 lädt die gegenteilige Annahme geradezu ein: Ein PFX in Base64 ist ein privater Schlüssel in Text. Die Kodierung ändert die Form des Secrets und nichts an seiner Geheimhaltung, daher ist ein Base64-PFX, das in ein Chat-Fenster, ein Ticket oder ein Commit kopiert wurde, ein privater Schlüssel, der in ein Chat-Fenster, ein Ticket oder ein Commit kopiert wurde. Behandeln Sie die Textform mit exakt derselben Sorgfalt wie die binäre Form, und bevorzugen Sie den Zertifikatspeicher oder einen Secrets-Manager gegenüber beiden.
Basic Auth, Data-URIs und die alten Gewohnheiten
Base64 ist älter als das Standarddokument, das ihm seinen Namen gab. Die MIME-Familie der RFCs aus 1996 brachte es in die E-Mail, und die HTTP-Basic-Authentifizierung brachte es in jeden Header-Austausch des frühen Webs, wo der Client das Anmeldedaten-Paar immer noch als einen Base64-String kodiert:
$credential = [System.Text.Encoding]::UTF8.GetBytes("alice:s3cret!")
[System.Convert]::ToBase64String($credential)
# YWxpY2U6czNjcmV0IQ==
# gesendet als: Authorization: Basic YWxpY2U6czNjcmV0IQ==
Das Standard-Alphabet ist hier das richtige, Plus und Schrägstrich inklusive, denn ein Header ist keine URL und braucht das sichere Alphabet nicht. Der gleiche Mechanismus erscheint in Data-URIs, dem Weg, auf dem ein Dokument sein eigenes Binary inline einbettet, und die Form ist ein wörtliches Präfix plus das Standard-Base64 der Bytes:
$dataUri = "data:application/octet-stream;base64," + [System.Convert]::ToBase64String([byte[]](1, 2, 3, 250, 251))
# data:application/octet-stream;base64,AQID+vs=
Beide Gewohnheiten lohnt es sich zu kennen, weniger als Dinge, die Sie bauen werden, sondern mehr als Dinge, auf die Sie treffen werden: Wenn ein Header oder ein Link eine lange Base64-Kette enthält, sind diese beiden Formate die ersten, die Sie prüfen, und beide sind nur ein einfaches Dekodieren davon entfernt, was sie sagen. Was der Punkt des Formats ist, und der Grund, warum die Dekodier-Seite dieser Site existiert.
Kodierte Befehle und die Windows-Werkzeugkiste
PowerShell hat seit Version 1.0 einen eingebauten Grund zum Kodieren: den -EncodedCommand-Parameter des Hosts selbst. Sie geben pwsh einen Base64-String, er dekodiert die Bytes als UTF-16LE, und das Ergebnis läuft als Befehl. Der dokumentierte Zweck sind Befehle, die mit dem Quoting der äußeren Shell kämpfen, und die Kodier-Seite ist zwei Zeilen:
$command = "Write-Host 'Hello from the encoded side'"
$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command))
# VwByAGkAdABlAC0ASABvAHMAdAAgACcASABlAGwAbABvACAAZgByAG8AbQAgAHQAaABlACAAZQBuAGMAbwBkAGUAZAAgAHMAaQBkAGUAJwA=
pwsh -NoProfile -EncodedCommand $encoded
# Hello from the encoded side
Lesen Sie die Kodier-Zeile sorgfältig, denn sie ist die, die jeder falsch macht: Der Payload muss UTF-16LE sein, und das ist die Unicode-Kodierung, nicht UTF-8. Kodieren Sie mit der falschen, und der Host dekodiert Ihre Bytes trotzdem als UTF-16LE und führt einen aus Mojibake bestehenden Befehl aus und produziert einen Fehler, der ein perfektes Porträt des Fehlers ist. Der Dekodier-Artikel deckt diesen Fehler vollständig ab, und die Lösung auf dieser Seite ist ein einzelnes Wort: Unicode.
Außerhalb der Sprache tragen die nativen Tools jeweils ihre eigene stille Kodierungsentscheidung. Auf Windows erzeugt certutil -encode infile outfile.b64 eine Standard-Base64-Datei mit den Panzerungszeilen, die PEM erwartet, -f überschreibt eine vorhandene Ausgabe, und das Flag, das sich zu merken lohnt, ist -unicodetext, das certutil die Ausgabedatei in Unicode schreiben lässt (laut Microsoft-Doku: "Ausgabedatei in Unicode schreiben") - ein Schalter, der eine Kodierungsentscheidung versteckt. Auf Linux ist das klassische Dienstprogramm base64 -w 0 file, wobei -w 0 der tragende Teil ist: ohne es bricht GNU base64 bei 76 Zeichen um und gibt Ihnen eine MIME-artige Datei, wenn Sie eine Zeile wollten. Auf macOS braucht die BSD-Variante ein solches Flag nicht, denn sie gibt standardmäßig eine ununterbrochene Zeile aus.
Kodieren, wenn die Ausgabe riesig ist
Für Alltagsgrößen ist die alles-lesen-alles-kodieren-Pipeline die schnelle und einfache, und sie ist die richtige, bis die Datei zu groß ist, um sie bequem im Speicher zu halten, oder die Daten stückweise von einem Download oder Socket ankommen. Dann ist das dokumentierte Werkzeug das Streaming-Paar: System.Security.Cryptography.ToBase64Transform, in einem CryptoStream verpackt, in das Sie rohe Bytes schreiben und aus dem Base64-Text herauskommt, wobei zu jedem Zeitpunkt nur ein kleiner Puffer am Leben ist:
$source = [System.IO.File]::OpenRead("./photo.png")
$destination = [System.IO.File]::Create("./photo.b64")
$transform = [System.Security.Cryptography.ToBase64Transform]::new()
$stream = [System.Security.Cryptography.CryptoStream]::new($destination, $transform, [System.Security.Cryptography.CryptoStreamMode]::Write)
$buffer = New-Object byte[] 65536
while (($read = $source.Read($buffer, 0, $buffer.Length)) -gt 0) {
$stream.Write($buffer, 0, $read)
}
$stream.Dispose()
$source.Dispose()
$destination.Dispose()
Ein Unterschied zur Einmal-Methode gehört aufgeschrieben: Der Stream produziert eine durchgehende Zeile ohne jeglichen Umbruch, egal wie groß der Input ist. Ein Dekodierer, der Leerraum ignoriert, macht sich nichts daraus, aber wenn das Ziel eine PEM-Datei ist, fahren Sie die 64-Spalten-Schleife aus dem Umbruch-Abschnitt danach über das Ergebnis. Und in C# ist die Standardform dasselbe ToBase64Transform + CryptoStream-Muster, das Sie im C#-Artikel sehen; PowerShell steuert es direkt, wie oben.
Wo kodierte Payloads schiefgehen
- Den String zu kodieren, nicht die Bytes.
ToBase64String("Hello")wirft eine Konversions-Exception, und die Methode sagt Ihnen damit, dass die erste Entscheidung, die Kodierung, noch nicht getroffen wurde. Machen Sie sie im Skript sichtbar, und der Fehler verschwindet. - UTF-16, wo UTF-8 versprochen wurde. Der Payload ist doppelt so lang wie erwartet, und der Konsument liest Müll. Die Kodierung ist Teil des Protokolls, und für fast jeden Draht des modernen Internets sagt das Protokoll UTF-8.
- Das 5.1-Lesen. Windows PowerShell 5.1 liest eine Textdatei ohne BOM mit der ANSI-Codepage der Maschine, bevor Ihr Skript sie je sieht, daher kann eine UTF-8-Quelldatei beschädigt werden, bevor der Kodierungsschritt kommt. Auf 5.1 Text mit einem expliziten UTF-8-Lesen lesen und die ersten Zeichen des Ergebnisses prüfen.
- Die falsche Umbruch-Breite. MIME will 76, PEM will 64, APIs wollen keine, und ein strenger Konsument behandelt einen unerwarteten Zeilenumbruch als fremdes Zeichen. Wählen Sie die Breite aus dem Konsumenten heraus und sagen Sie es in einem Kommentar.
- Padding auf der falschen Seite des Tauschs. Die Gleichheitszeichen wegzulassen ist für base64url-Tokens richtig und für einen Konsumenten, der Standard-Padding erwartet, falsch. Der Alphabet-Tausch und die Padding-Entscheidung sind zwei Wahlmöglichkeiten, nicht eine.
- Das Umformatieren dessen, was Sie signiert haben. Eine JWT-Signatur deckt den exakten JSON-Text ab, einschließlich Schlüsselreihenfolge und Abstände. Sortieren Sie die Claims um oder fügen Sie ein Leerzeichen hinzu, und das Token hört auf, sich zu verifizieren, ohne dass eine Fehlermeldung irgendwo in der Nähe der Ursache wäre.
- Werte über 255. Die Umwandlung einer ganzen Zahl in ein Byte wirft eine Exception, statt zu wrapen, daher stoppt 256 das Skript beim Cast. Wenn Ihre Quelldaten Zahlen sind, casten Sie explizit und lassen Sie einen Fehler ein Fehler sein, den Sie sehen.
- Dem Kostüm zu glauben. Base64 ist keine Verschlüsselung und keine Kompression: Es ist eine Übersetzung, die die Daten um ein Drittel wachsen lässt. Ein Secret in Base64 ist ein Secret in Klartext, und eine Datei in Base64 ist eine Datei, die 33 Prozent mehr Platz braucht.
Regeln für Encoder, denen Sie vertrauen können
- Erzeugen Sie die Bytes bewusst. Die erste Zeile eines jeden Kodierungsskripts sollte ein explizites
GetBytesoder ein Byte-Lesen sein, nie die Hoffnung, dass PowerShell einen String in die richtigen Bytes umwandelt. - Nennen Sie das Alphabet und die Breite in einem Kommentar neben dem Code, der die Wahl trifft: standard oder base64url, umgebrochen bei 76, 64 oder gar nicht. Der Konsument ist ein Mensch, der das Skript in sechs Monaten liest, und dieser Mensch sind Sie.
- Schreiben Sie die Textdatei mit
-NoNewline, es sei denn, der Konsument erwartet spezifisch einen abschließenden Umbruch, und wählen Sie das Zeilenende (LF oder CRLF) so, wie es die Dokumentation des Konsumenten erwartet. - Testen Sie die Hin- und Rückreise, während Sie bauen: kodieren, dekodieren, Bytes vergleichen. Ein dreißigsekündiger
Compare-Objectüber die beiden Byte-Arrays fängt Kodierungsfehler, Umbruchfehler und Byte-Reihenfolge-Fehler alle auf einmal ab, solange die Ursache noch frisch ist. - Loggen Sie Größen, keine Payloads. Die Byte-Anzahl vorher und die Zeichen-Anzahl nachher sollten bei einem Verhältnis von etwa 1,33 liegen, und wenn sie das nicht tun, sagt Ihnen die Größen-Diskrepanz, wo Sie hinschauen sollen, ohne dass das Log je die Daten enthält.
Wie PowerShell seinen Encoder geerbt hat
Die kürzeste wahre Geschichte von Base64-Kodierung in PowerShell ist, dass PowerShell nie eines geschrieben hat. Die Methode, die Sie aufrufen, Convert.ToBase64String, erschien mit .NET Framework 1.1 im Jahr 2003, und jedes PowerShell seit Version 1.0 im November 2006 hat schlicht das .NET offengelegt, auf dem es läuft. Das Projekt hieß während der Entwicklung Monad, wurde zum ersten Mal öffentlich auf der Professional Developers Conference im Oktober 2003 gezeigt, und bis zum Release war der .NET-Encoder, den es einpackt, bereits drei Jahre alt und trug Web-Verkehr.
Das Format wurde im selben Jahr standardisiert, in dem die Shell an den Start ging. RFC 4648, veröffentlicht im Oktober 2006, legte das Alphabet, die Padding-Regeln, die Strenge des Dekodierens und die base64url-Variante fest, und es beschreibt immer noch exakt das Verhalten, das das .NET-Paar implementiert. Die MIME-RFCs, die ihm im Jahr 1996 vorausgingen, hatten den 76-Zeichen-Umbruch bereits in die E-Mail gebracht, daher ist diese Breite bis heute der Standard von InsertLineBreaks. Als PowerShell im August 2016 als PowerShell Core open source und plattformübergreifend wurde, kam der Encoder zu Linux und macOS unverändert mit, denn es gab nichts zu ändern.
Was sich später änderte, passierte in .NET, und größtenteils außerhalb der Reichweite von PowerShell. Die Runtime bekam in den neueren Versionen schnellere, span-basierte Base64-Helfer, einschließlich der Base64Url-Klasse und der Try-präfixierten Dekodier-Methoden. Spans sind byref-ähnliche Typen, und ältere PowerShell-Releases konnten wirklich überhaupt nicht an sie binden, aber der Method-Binder des aktuellen PowerShell führt jetzt eine implizite Array-zu-Span-Umwandlung durch, daher sind diese Kurzwege von einem Skript aus aufrufbar, auf einem neuen genug Host. Die Community-Antwort auf alles Ältere ist das Microsoft.PowerShell.TextUtility-Modul aus der PowerShell Gallery, dessen ConvertTo-Base64 dieselbe .NET-Methode einpackt und einen -Text-Parameter mit UTF-8-Standardwert plus einen -InsertBreakLines-Schalter für den 76-Spalten-Umbruch hinzufügt. Installieren Sie es mit Install-Module -Name Microsoft.PowerShell.TextUtility, wenn Sie die Cmdlet-Form bevorzugen, und beachten Sie, dass das Modul archiviert ist und nicht mehr aktiv gepflegt wird, was ein weiterer Grund ist, warum die eingebaute Methode die Empfehlung für neue Skripte bleibt.
Die Zahlen und Namen, die sich zu merken lohnen
- Jede drei Eingabe-Bytes werden zu vier Ausgabe-Zeichen, daher wird kodiertes Material etwa 33 Prozent größer als das Original, und das Padding ist niemals mehr als zwei Gleichheitszeichen.
- Die Standardausgabe ist eine ununterbrochene Zeile.
InsertLineBreaksbricht bei 76 Zeichen mit CRLF um, die MIME-Konvention aus 1996. PEM will 64, und kein eingebautes Flag erzeugt diese Breite. - base64url ist Standard-Base64, bei dem Plus und Schrägstrich gegen Bindestrich und Unterstrich getauscht werden, Padding meist weggelassen, und es ist das Alphabet jedes JWT und API-Tokens.
- "Café" sind fünf Bytes in UTF-8 und acht in UTF-16LE. Derselbe sichtbare Text, doppelte Größe, und die beiden Kodierungen sind über den Draht nicht austauschbar.
-EncodedCommandexistiert seit dem ersten PowerShell-Release, und sein Payload muss UTF-16LE sein, nicht UTF-8. Das einzelne Wort, das den häufigsten Fehler auf dieser Seite behebt, istUnicode.certutil -encodekann eine Kodierungsentscheidung innerhalb von-unicodetextverstecken, und GNUbase64braucht-w 0, um Ihnen eine Zeile zu geben, statt des 76-Spalten-Umbruchs.- Die span-basierten Base64-Helfer von .NET, einschließlich
Base64Url, waren aus PowerShell einmal nicht erreichbar, denn Spans sind byref-ähnliche Typen, die der ältere Method-Binder nicht binden konnte. Das aktuelle PowerShell (7.4+, auf einer .NET-Runtime, die neu genug ist, um die Klasse mitzuliefern) löst ein Array-Argument gegen einen Span-Parameter auf, ohne Murren, also funktioniert der direkte Aufruf heute - aber der Tausch der zwei Zeichen bleibt das eine Rezept, das in jeder Version funktioniert, alt und neu gleichermaßen. - Ein einzelnes Byte, 123, wird zu
ew==kodiert: das kleinste mögliche Beispiel für die Regel, dass die Länge der Ausgabe Ihnen die Länge des Inputs verrät.
Den Pfeil umdrehen
Alles in diesem Artikel dreht sich darum, Daten, die Sie in der Hand haben, in einen Base64-String zu verwandeln. Die Spiegeloperation, einen String zu nehmen und Ihre Daten zurückzubekommen, hat ihre eigene Besetzung an Problemen: ein Dekodierer, der vier Arten von Leerraum ignoriert, eine Fehlermeldung, die drei Verbrechen abdeckt, ein JWT, in das man einen Blick wirft, ein Zertifikat, das ausgepackt wird, und ein -EncodedCommand, das erklärt wird. Diese Richtung bekommt ihre eigene vollständige Behandlung, mit ihren eigenen Fallen und ihrer eigenen Geschichte, im verwandten Artikel auf der Schwestersite, Base64-Dekodierung in PowerShell, unten verlinkt.
Zuletzt aktualisiert: 2026-09-08
Verwandter Artikel: Base64-Dekodierung in PowerShell: Ein vollständiger Leitfaden