Base64-decodering in PowerShell: een complete gids
Irgens in een logregel, een configbestand of een foutmelding loop je er tegenaan: een lange reeks letters en cijfers, met af en toe een plus of schuine streep, en een of twee gelijktekens die verdacht genoeg aan het einde parkeren. Het lijkt op ruis. Het is het niet. Het is Base64, en je weet al wat je wilt: de inhoud die het verbergt.
Base64 is een vertaling, geen compressie en geen slot. Het schrijft elke reeks bytes om naar afdrukbare tekst, vier tekens per drie invoerbytes (de gecodeerde data loopt daardoor zo'n 33% groter dan het origineel), met een alfabet van 64 tekens en het gelijkteken als afsluitende padding. De startpagina van deze site loopt het alfabet, de bitrekening en de varianten volledig door, dus besteedt dit artikel zijn tijd waar PowerShell het verschil maakt: de ene .NET-methode die je gaat aanroepen, de regels die het handhaaft, en een tiental hoekjes uit het echte werk waar decoderen in PowerShell interessant wordt.
De methode en haar contract
PowerShell levert geen eigen Base64-cmdlet mee. Het werk wordt verricht door een methode op een .NET-class die sinds .NET Framework 1.1 in 2003 deel uitmaakt van het framework, drie jaar voordat PowerShell zelf verscheen:
$bytes = [System.Convert]::FromBase64String("SGVsbG8sIFdvcmxkIQ==")
[System.Text.Encoding]::UTF8.GetString($bytes)
# Hello, World!
Dat is de hele API: één tekenreeks erin, één byte-array eruit. Het werkt in elke PowerShell op elk besturingssysteem, in Windows PowerShell 5.1 en in PowerShell 7 op Windows, Linux en macOS, want het is simpelweg .NET. Het contract is kort genoeg om te onthouden, dus hier is het als tabel:
| Invoer | Wat je terugkrijgt |
|---|---|
$null |
Een lege array, geen fout. PowerShell verandert $null stilletjes in een lege tekenreeks vóór de aanroep |
| Een lege tekenreeks | Een lege array, geen fout |
| Een geldige payload | Een byte[], nooit een tekenreeks, zelfs niet als de data tekst is |
| Een ongeldige payload | Een FormatException, voor je verpakt in een MethodInvocationException |
Eén waarschuwing vóórdat je enige foutafhandeling schrijft: die FormatException heeft een enkele melding die drie verschillende zonden dekt. Een teken buiten het alfabet, meer dan twee paddingtekens, of een niet-witruimte-teken dat zich tussen de padding verbergt: alles levert precies dezelfde zin op. Als je hem ziet, zegt de melding je niet welke van de drie jij beging, dus ga je terug en lees je je invoer:
try {
[System.Convert]::FromBase64String("SGV!G8s=")
}
catch {
$real = $_.Exception.InnerException
$real.GetType().Name
# FormatException
$real.Message
}
En de veelvoud-van-vier-regel heeft een randje dat mensen de eerste keer verbaast. Vier tekens zonder enige padding is volkomen geldig; het betekent alleen dat de overbodige bits in het laatste teken worden weggegooid. Drie tekens is geen veelvoud van vier, en wordt afgewezen:
[System.Convert]::FromBase64String("SGVs").Count
# 3: vier tekens zonder padding is prima
[System.Convert]::FromBase64String("SGV")
# FormatException: drie tekens is geen veelvoud van vier
Wat de decoder wel en niet accepteert
De decoder is streng over het alfabet en soepel over precies één ding. De geldige tekens zijn de 64 Base64-cijfers (A t/m Z, a t/m z, 0 t/m 9, plus en schuine streep) en het gelijkteken als afsluitende padding. Precies vier witruimte-tekens worden genegeerd, waar en hoe vaak dan ook: de tab, de line feed, de carriage return en de spatie. De officiële .NET-documentatie noemt ze bij hun Unicode-namen, wat je vertelt dat dit een gedocumenteerde garantie is en geen geluksaccident.
In de praktijk is dit een superkracht. MIME, de e-mail-encoding die Base64 op de kaart zette, breekt gecodeerde regels af op 76 tekens, dus een payload die door e-mail, een ticket of een logbestand gereist is, komt meestal gebroken over veel regels aan. De decoder maakt het zich niet. Plak het zoals het is:
$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-tekst komt aan
# in e-mail omgebroken op 76 kolommen, dus de decoder moet dat negeren.
Alles wat geen alfabetteken is, is een harde stop. De meest voorkomende overtreders in het wild zijn de niet-breekbare spatie (de lieveling van tekst die van webpagina's is geplakt) en de byte-order mark (het onzichtbare teken dat je achterna loopt als een bestand met de verkeerde tekenset werd gelezen). Geen van beide is witruimte voor zover deze methode betreft, dus gooien ze allebei:
try {
[System.Convert]::FromBase64String("SGVs`u{00A0}G8=")
}
catch {
$_.Exception.InnerException.GetType().Name
# FormatException
}
De strikte aanpak is opzettelijk, geen kwaadheid. RFC 4648, de standaard die Base64 in 2006 codificeerde, zegt dat implementaties niet-alfabettekens moeten afwijzen tenzij het protocol expliciet soepelheid toestaat, want een decoder die vreemde tekens stilletjes doorlaat, kan omgebouwd worden tot een sluik kanaal om data te smokkelen onder alles door dat alleen het alfabet inspecteert. De .NET-decoder volgt de strenge regel, en meestal wil je dat ook.
Een byte-array is geen tekenreeks
De methode stopt opzettelijk bij het byte-array. Wat die bytes betekenen is een tweede beslissing die alleen jij kunt nemen, en het verkeerd raden is de beroemdste fout in PowerShell-Base64-werk. De standaardaannname, UTF-8, klopt voor vrijwel alles op internet, en de rondreis is twee aanroepen:
$bytes = [System.Convert]::FromBase64String("SGVsbG8sIFdvcmxkIQ==")
[System.Text.Encoding]::UTF8.GetString($bytes)
# Hello, World!
De tekensets die je in de praktijk gaat pakken, en wat elk ervan doet als je het verkeerd raadt:
| Tekenset | Gebruik het als | Als je het verkeerd raadt |
|---|---|---|
UTF8 |
Web-API's, JSON, JWT's, kortom alles uit het moderne. De veilige standaard | Latin-1- of UTF-16-tekst komt terug als mojibake |
Unicode (UTF-16LE) |
De payload komt uit Windows-werktools, een registerwaarde of een .NET-tekenreeks die vóór het versturen werd gecodeerd | Elk teken krijgt een gat eromheen, omdat je één byte leest waar er twee bedoeld waren |
ASCII |
Klassieke HTTP Basic-aanmeldgegevens en andere protocollen die gegarandeerd 7 bits zijn | Alles met een waarde boven 127 wordt een vraagteken |
Latin1 |
Oudere Europese tekst van vóór UTF-8 | Meerbytes-UTF-8-reeksen splitsen op in een paar verkeerde letters |
Default |
Bijna nooit. Het is de systeemeicodepagina van de machine | Je script gedraagt zich anders bij elke Windows-regio-instelling |
De klassieke mislukking is UTF-8-tekst die als UTF-16 wordt gedecodeerd. De bytes zijn echt, de methode is tevreden, en het resultaat is toch onzin:
# "SGk=" is de UTF-8-bytes van "Hi"
[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String("SGk="))
# Een enkel onleesbaar teken: 2 bytes UTF-8 gelezen als één 2-byte UTF-16-eenheid
De praktische regel: als de gedecodeerde tekst eruitziet alsof elk teken een onzichtbaar gat eromheen heeft, of alsof het een ander alfabet is, zit je één tekenset ernaast. Vraag waar de data is geproduceerd, en twijfel je, vertrouw dan op UTF-8 maar controleer met je eigen ogen de eerste paar tekens. En beslis de tekenset vóór dat je decodeert, niet nádat het mojibake in je log verschijnt.
base64url: het alfabet dat zich net gedraagt in URLs
Je zult een neef van Base64 tegenkomen in elke API-token, elke JWT en elke in een URL ingebedde identifier die je ooit zult aanraken. De plus en de schuine streep van standaard Base64 zijn in een URL alleen legaal na percent-encoding, en de padding met gelijktekens lijkt op een veldscheider. Daarom definieerde RFC 4648 een URL- en bestandsnaam-veilig alfabet: dezelfde 64 tekens, behalve dat plus het koppelteken wordt en de schuine streep de lage streep. De padding wordt meestal helemaal weggegooid, want de lengte van de data maakt hem overbodig. De RFC is er zorgvuldig in dat je deze variant base64url noemt en niet gewoon "base64", en de rest van deze sectie volgt dat.
.NET levert wel een eigen class voor dit alfabet mee, System.Buffers.Text.Base64Url, toegevoegd in .NET 9 met snelle encodeer- en decodeermethoden die volledig rond ReadOnlySpan<T>-parameters zijn gebouwd. Actueel PowerShell (7.4 en later, zodra het draait op een .NET-versie die de class meelevert) kan deze overloads die spans als argument verwerven vandaag de dag inderdaad direct aanroepen, dankzij een impliciete array-/tekenreeks-naar-span-conversie die de method-binder nu uitvoert, zodat [System.Buffers.Text.Base64Url]::DecodeFromChars("--__AQI") zonder omhaal werkt. Dat was niet altijd zo: Windows PowerShell 5.1 en oudere PowerShell 7.x-releases konden helemaal niet binden aan span-parameters, en de class bestond vóór .NET 9 in de eerste plaats gewoon niet, dus elk script dat moet draaien op 5.1, een oudere 7.x of een host vóór .NET 9 heeft nog steeds de draagbare versie nodig: wissel de twee tekens om, en herstel de padding vóór je de tekst aan de standaarddecoder geeft. De padding die je moet toevoegen is wat de lengte tot een veelvoud van vier maakt:
$token = "--__AQI" # base64url, zonder 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
Twee valkuilen zitten in dat blokje. Eerst de padding-wiskunde: een payload waarvan de lengte al een veelvoud van vier is, heeft geen padding nodig, en de -eq 4-check is wat de expressie eerlijk houdt. Tweedens de richting: als je alleen decodeert, voeg je padding toe en wissel je om; je verwijdert nooit padding uit standaard-Base64-invoer, want standaarddecoders verwachten dat die er is. Als de bron een JWT of een API-token is, is het base64url zonder padding, en is het recept hierboven precies de vorm die je zoekt.
Een JWT openen zonder de sleutels
Een JSON Web Token is drie base64url-segmenten, aan elkaar gekoppeld door punten: header, payload, handtekening. De eerste twee zijn gewoon JSON, en Base64 is geen versleuteling, dus wie het token heeft kan beide lezen. Dat is een pluspunt, geen gebrek: het token is ontworpen om te inspecteren, en de handtekening is wat het onnavoegbaar maakt. PowerShell houdt het peepje op drie regels:
$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
# eigenschap name:
(Decode-UrlSegment $parts[1] | ConvertFrom-Json).name
# John Doe
Drie dingen om je te herinneren. Het derde segment, de handtekening, is ook base64url, maar het decodeert naar binaire handtekening-bytes, geen tekst, dus verwacht daar geen mooi JSON. De header vertelt meestal alleen welk algoritme het token heeft getekend (HS256, RS256, ...), en een header die none zegt is een rode vlag, geen gemak. En de payload lezen is hem niet vertrouwen: base64 laat je de claims zien, alleen de handtekening maakt ze authentiek. Als je taak is om tokens te accepteren, verifieer dan de handtekening met de sleutel van de uitgever; als je taak is om er een te debuggen, heb je de code hierboven helemaal genoeg.
Bestanden, PEM en de lange weg naar bytes
De meest voorkomende bestandsvorm is een tekstbestand met daarin Base64 van iets groters: een back-up-blob, een gedownload binair bestand, een geserialiseerd object. De rondreis is vier regels, en de moderne manier om de uitvoer te lezen is een écht byte-array, geen tekstgok:
$encoded = Get-Content -Path ./payload.b64 -Raw
$encoded = $encoded.Trim()
$bytes = [System.Convert]::FromBase64String($encoded)
[System.IO.File]::WriteAllBytes("./payload.bin", $bytes)
$bytes.Length
# hoeveel bytes de tekst meedraagt
Het oorspronkelijke binair bestand teruglezen is waar PowerShell 6 en nieuwer zich betaald maken. De -AsByteStream-parameter leest ruwe bytes, en met -Raw reikt het je in één keer een echte byte[] aan:
$bytes = Get-Content -Path ./photo.png -AsByteStream -Raw
$encoded = [System.Convert]::ToBase64String($bytes)
Set-Content -Path ./photo.b64 -Value $encoded -NoNewline
$bytes.Length
# originele grootte, vóór de tekstbelasting van 33 procent
Laat -Raw weg en je krijgt een stroom van losse byte-objecten (een Object[] als je het vangt), prima voor inspectie maar verkeerd om door te geven aan .NET-methoden die een array verwachten. En Windows PowerShell 5.1 heeft helemaal geen -AsByteStream, dus op 5.1 is het betrouwbare lezen [System.IO.File]::ReadAllBytes(), dat overal bestaat.
PEM is de gepantserde neef die je kent van elk certificaat en elke private key: een standaard-Base64-lichaam, meestal omgebroken op 64 tekens, tussen -----BEGIN ...- en -----END ...-regels. De pantsering is tekst; het lichaam is de payload. Strip de pantsering, voeg de regels samen, decodeer:
$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
# de binaire DER-grootte van het certificaat
Omdat de standaarddecoder witruimte sowieso negeert, is -join "" dubbele zekerheid in plaats van vereiste, maar het script expliciet houden over wat het weghaalt, zorgt ervoor dat het zich op elke machine en bij elke regelafsluitingsconventie hetzelfde gedraagt. De andere richting, DER-bytes inpakken naar PEM, is gewoon de Base64-encoder plus twee regels tekst, en het encodeer-artikel op de zustersite toont de 64-koloms-omwikkeling volledig.
Certificaten en de Windows-toolbox
Certificaten zijn de zwaarste Base64-burgers in het dagelijkse werk, en PowerShell kan het hele gezin vasthouden. Een PFX-bestand is een binair pakket van certificaat plus private key, en het is het formaat dat je het vaakst aantreft als Base64-tekst in configbestanden en deployment-scripts. Het terugdecoderen naar een live certificaat is een one-liner met het .NET-type, en het werkt cross-platform 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
# wanneer het niet meer waar is
PowerShell 7 levert ook Get-PfxCertificate mee, die een PFX-bestand rechtstreeks van schijf leest met een -Password-parameter, dus voor bestanden op schijf kun je de handmatige decode helemaal overslaan. Een kaal certificaat (zonder key) is nog eenvoudiger: de DER-bytes gaan rechtstreeks in hetzelfde X509Certificate2-type, zonder enig wachtwoord.
Buiten de taal zijn twee native tools de moeite waard om te kennen. Op Windows decodeert certutil -decode infile.b64 outfile een Base64-bestand met file-in/file-out-semantiek (voeg -f toe om te overschrijven), waardoor het de standaard is voor snelle oplossingen in een gewoon commando-venster. Zijn broer certutil -encode heeft een flag die de moeite waard is om te onthouden: -unicodetext zet de invoertekst om naar UTF-16 vóór dat deze naar Base64 wordt geëncodeerd, en verstopt zo een hele tekensetbeslissing in één switch. Op Linux en macOS is de klassieke utility base64 -d, die een bestand of de standaardinvoer decodeert en regeleinden standaard overslaat; voeg bij GNU coreutils -i toe als de payload ook spaties, tabs of CRLF uit Windows-mail meedraagt.
Commando's in een Base64-omhulling
PowerShell heeft sinds versie 1.0 een ingebouwde reden om Base64 te spreken: de -EncodedCommand-parameter van de host zelf. Je geeft pwsh een Base64-tekenreeks, die decodeert de bytes als UTF-16LE, en het resultaat wordt als commando uitgevoerd. Het officiële doel, recht uit de documentatie, is om commando's in te dienen die complexe aanhalingstekens of haakjes nodig hebben, zonder te vechten met de quote-regels van de buitenste shell:
$command = "Write-Host encoded-hello"
$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command))
# VwByAGkAdABlAC0ASABvAHMAdAAgAGUAbgBjAG8AZABlAGQALQBoAGUAbABsAG8A
pwsh -NoProfile -EncodedCommand $encoded
# encoded-hello
Lees die tweede regel goed, want daar struikelt iedereen: de payload moet UTF-16LE zijn, dat is [System.Text.Encoding]::Unicode. Codeer je het commando in plaats daarvan als UTF-8, dan decodeert PowerShell het met plezier als UTF-16LE en voert het een commando van mojibake uit, en de foutmelding die het oplevert is een perfect portret van de fout:
$wrong = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($command))
pwsh -NoProfile -EncodedCommand $wrong
# Fout: een muur van onleesbare karakters, "De term ... wordt niet herkend..."
Dit zelfde mechanisme is de reden waarom securityteams zich zorgen maken over Base64 in PowerShell. Een lange ondoorzichtige token die aan -EncodedCommand wordt meegegeven is een veelvoorkomende vorm in geautomatiseerde tooling, en precies daarom decodeeren endpoint-beschermingsproducten deze payloads vóórdat ze draaien: niets aan Base64 houdt het commando verborgen voor een decoder, het houdt het alleen verborgen voor een mens dat een proceslijst leest. Genereer je gecodeerde commando's voor je eigen automatisering, houd dan het originele commando naast de token, want de token zelf zal je om 3 uur 's nachts niets uitleggen.
Decoderen als de invoer enorm is
Voor alledaagse maten is de aanpak met de ene methode de snelle. Een binair bestand van vijf megabyte wordt een tekenreeks van zo'n 6,9 miljoen tekens, en de decode van die tekenreeks duurt enkele milliseconden op een moderne machine. De eigen noot van de .NET-documentatie is dat FromBase64String is ontworpen om één tekenreeks te verwerken die al de data bevat, en dat klopt, en dat is ook prima tot aan erg grote grenzen, omdat de methode in-place werkt op de tekenreeks zonder betekenisvolle extra kopieën.
Is de payload groter dan je prettig zou vinden in één tekenreeks, of komt hij aan als een stroom (een download, een socket, een enorm log), dan is het gedocumenteerde instrument System.Security.Cryptography.FromBase64Transform verpakt in een CryptoStream: je voert Base64-tekst toe en leest gedecodeerde bytes uit, en op elk moment is slechts een klein buffer actief. Let op dat TransformStream, de C#-hulpmethode hiervoor, een extension method is, en PowerShell ziet extension methods niet, dus instantieer je de CryptoStream direct:
$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()
Voor de negentig procent van de klussen is de simpele weg nog steeds de juiste: lees het hele tekstbestand in met Get-Content -Raw, trim het, decodeer het, schrijf de bytes. Pak de stream-versie als het bestand te groot is om comfortabel in het geheugen te houden, of als de data stukje bij beetje aankomt. En probeer niet te lussen over regels en elke regel apart te decoderen: Base64-groepen van vier tekens respecteren je regeleinden niet, dus een regel die een groep in het midden splitst decodeert niet op zichzelf. Lees de hele tekst, en decodeer dan één keer.
Valkuilen die namiddagen kosten
- De tekenset-gok. UTF-8 gelezen als UTF-16, of Latin-1 gelezen als UTF-8, levert overtuigde mojibake op. Bepaal de tekenset uit de bron van de data, ga standaard uit van UTF-8, en bekijk de eerste paar gedecodeerde tekens vóórdat je de rest vertrouwt.
- Onzichtbare tekens van het web. Een niet-breekbare spatie of een byte-order mark die van een pagina of een rich-text-mail is geplakt, is voor de decoder een vreemd teken en gooit de generieke
FormatException. Stuur de invoer door.Trim()en een check op niet-afdrukbare tekens vóórdat je decodeert. - Paddingverwarring. Standaard Base64 komt aan met
=of==aan het eind; base64url van tokens komt er zonder. Eén van beide voeden aan het recept dat voor de andere is gebouwd, is de meest voorkomende stille breuk in API-werk, en de lengtecheck in de base64url-sectie is de wacht. - De ene melding, drie misdaden. Omdat de
FormatException-melding slechte tekens, te veel padding en vuile padding allemaal tegelijk dekt, sturen catch-blokken die alleen de melding loggen je in cirkels rond. Log ook de lengte van de invoer en het eerste overtredende stukje. - Een tekenreeks terug verwachten. Het resultaat is altijd een byte-array. Het moment dat je het direct als tekenreeks gaat formatten, krijg je een lijst van getallen, geen tekst. Converteer met een expliciete tekenset, één keer, aan het eind.
- De 5.1-bestandsstandaard. Windows PowerShell 5.1 leest bestanden zonder BOM met de ANSI-codepagina van het systeem, terwijl PowerShell 7 UTF-8 veronderstelt. Leest je script het Base64-tekstbestand op 5.1 en is het bestand UTF-8 met non-ASCII rond de payload, dan gebeurt de beschadiging vóórdat de decoder het ooit te zien krijgt.
- Base64 behandelen als een slot. Het is een vertaling. Een wachtwoord, token of geheim in Base64 is platte tekst in een vermomming, en elke decoder op de planeet, dit artikel inbegrepen, opent het in één regel.
Gewoonten die scripts eerlijk houden
- Trim externe invoer voordat je decodeert. Eén
.Trim()haalt meer productieproblemen weg dan welke foutafhandeling dan ook. - Valideer voordat je decodeert als de bron niet te vertrouwen is: na het weghalen van de vier toegestane witruimte-tekens zou de tekenreeks alleen alfabettekens moeten bevatten, met hooguit twee afsluitende gelijktekens. Een snelle regex-check verandert een mysterie-exceptie in een nette afgekeurde-invoer-melding.
- Houd de bytes tot de allerlaatste stap als bytes. Decodeer één keer, geef de
byte[]door aan de file-API of de encoder die hem nodig heeft, en converteer dan pas naar tekst met een welbewuste tekenset. - Log lengtes, niet payloads. De grootte van de invoer en de grootte van de gedecodeerde uitvoer vertellen je bijna alles over een decode-fout, zonder dat je mogelijk gevoelige data in het log plakt.
- Voor alles wat over een draad gaat, noteer in welk alfabet het staat, standaard of base64url, en welke paddingconventie, in dezelfde regel code die het decodeert. Toekomstige jij is de consument van die noot.
Hoe PowerShell zijn decoder erfde
De kortste waarheid over de geschiedenis van Base64 in PowerShell is dat PowerShell er nooit zelf één schreef. De methode die je gebruikt, Convert.FromBase64String, verscheen met .NET Framework 1.1 in 2003, en elke PowerShell sinds versie 1.0 in november 2006 heeft simpelweg het .NET blootgesteld waarop het draait. Het project heette Monad terwijl het werd gebouwd, voor het eerst publiek getoond op de Professional Developers Conference in oktober 2003, en tegen de tijd dat het werd uitgebracht was het .NET-encoder/decoder-paar dat het verpakt al drie jaar oud en in dagelijks gebruik.
Het formaat zelf werd gestandaardiseerd in hetzelfde jaar dat de shell verscheen. RFC 4648, gepubliceerd in oktober 2006, is het document dat het alfabet, de paddingregels, de verwachting van strikt decoderen en de base64url-variant vastlegde, en het beschrijft nog steeds exact het gedrag dat FromBase64String vandaag implementeert. Toen PowerShell in augustus 2016 open source en cross-platform werd als PowerShell Core, kwam de decoder zonder enige verandering mee op Linux en macOS, omdat er niets te veranderen was.
De enige echte toevoeging is het door de community onderhouden Microsoft.PowerShell.TextUtility-module uit de PowerShell Gallery, waarvan de ConvertFrom-Base64-cmdlet dezelfde .NET-methode verpakt en een -AsByteArray-switch toevoegt plus een tekststandaard die als UTF-8 decodeert. Installeer hem met Install-Module -Name Microsoft.PowerShell.TextUtility als je de cmdlet-vorm prefereert, maar één kanttekening: het module is nu gearchiveerd en niet meer actief onderhouden, wat nog een reden is waarom de ingebouwde methode de aanbeveling blijft voor nieuwe scripts.
Feiten die je je moet herinneren
- De decoder negeert tabs, line feeds, carriage returns en spaties overal in de invoer. Een honderd omgebroken regels decoderen exact zoals één lange regel.
- Zowel
$nullals de lege tekenreeks decoderen zonder klacht naar een lege array, watFromBase64Stringongewoon vergevingsgezind maakt aan de rand. - De ene
FormatException-melding dekt drie verschillende faalmodi. Als ze afvliegt, zit het antwoord in de invoer, niet in de melding. "SABpAA=="is de tekenreeksHiin de eigen interne tekenset van PowerShell, UTF-16LE. Hij is twee keer zo lang als de UTF-8-codering van dezelfde twee letters, en die verhouding is de vingerafdruk van Windows-native tekst in elke Base64 die je gaat lezen.-EncodedCommandbestaat sinds de eerste PowerShell-release, en de payload is verplicht UTF-16LE, geen UTF-8. Codeer met de verkeerde tekenset en de shell voert je mojibake met plezier uit.- De nieuwere span-gebaseerde Base64-hulpmiddelen van .NET, waaronder de
Base64Url-class, waren onbereikbaar vanuit oudere PowerShell-releases omdat spans byref-achtige typen zijn waaraan de method-binder niet kon binden. Dat is veranderd: actueel PowerShell (7.4 of later, op een .NET-versie die nieuw genoeg is om de class mee te leveren) lost een array- of tekenreeksargument op tegen eenReadOnlySpan<T>-parameter zonder klacht, dus de directe aanroep werkt vandaag. De twee-tekens-wissel verdient zijn kost als de versie die ook draait op Windows PowerShell 5.1 en oudere hosts, niet als de enige overgebleven weg. Get-Content -AsByteStreamzonder-Rawgeeft je een stroom van byte-objecten, geen byte-array. Voeg-Rawtoe en het type is precies wat de .NET-methoden verwachten.
De lange route
Alles in dit artikel gaat over een Base64-tekenreeks pakken en je data terugkrijgen. De spiegelbeweging, data omzetten naar Base64, lijkt een one-liner tot je het feit tegenkomt dat PowerShell-tekenreeksen geen bytes zijn, dat UTF-16 je grootte verdubbelt, dat regelomwikkeling twee conventionele breedtes heeft, en dat base64url-uitvoer zijn eigen twee-tekens-operatie nodig heeft. Die richting krijgt zijn eigen volledige behandeling, met zijn eigen valkuilen en zijn eigen geschiedenis, in het gerelateerde artikel op de zustersite, Base64 encoderen in PowerShell, dat hieronder gelinkt staat.
Laatst bijgewerkt: 2026-10-06
Gerelateerd artikel: Base64-codering in PowerShell: een complete gids