Vous avez à traiter le format Base64 ? Alors ce site est parfait pour vous ! Utilisez notre outil en ligne super pratique pour encoder ou décoder vos données.

Décodage Base64 en PowerShell : un guide complet

Quelque part dans une ligne de log, un fichier de config ou un message d'erreur, vous tombez dessus : une longue suite de lettres et de chiffres, avec de temps en temps un plus ou un slash, et un ou deux signes equals garés de façon suspecte à la fin. Ça a l'air du bruit. Ce n'en est pas. C'est du Base64, et vous savez déjà ce que vous voulez : la chose qu'il cache.

Le Base64 est une traduction, pas une compression et pas un cadenas. Il réécrit n'importe quelle séquence d'octets en texte imprimable, quatre caractères pour trois octets en entrée (donc les données encodées font environ 33 % de plus que l'original), avec un alphabet de 64 caractères plus le signe equals comme remplissage de fin. La page d'accueil de ce site détaille l'alphabet, les calculs en bits et les variantes en entier, donc cet article passe son temps là où PowerShell fait la différence : la seule méthode .NET que vous appellerez, les règles qu'elle impose, et la douzaine ou presque de coins du vrai travail où le décodage dans PowerShell devient intéressant.

La méthode et son contrat

PowerShell ne fournit pas de cmdlet Base64 de son propre chef. Le travail est fait par une méthode sur une classe .NET qui fait partie du framework depuis .NET Framework 1.1 en 2003, trois ans avant que PowerShell lui-même ne sorte :

$bytes = [System.Convert]::FromBase64String("SGVsbG8sIFdvcmxkIQ==")
[System.Text.Encoding]::UTF8.GetString($bytes)
# Hello, World!

C'est toute l'API : une chaîne en entrée, un tableau d'octets en sortie. Ça fonctionne sur chaque PowerShell de chaque système d'exploitation, que ce soit Windows PowerShell 5.1 ou PowerShell 7 sur Windows, Linux et macOS, parce que c'est simplement du .NET. Le contrat est assez court pour le mémoriser, alors le voici sous forme de tableau :

Entrée Ce que vous obtenez
$null Un tableau vide, pas d'erreur. PowerShell transforme silencieusement $null en chaîne vide avant l'appel
Une chaîne vide Un tableau vide, pas d'erreur
Un payload valide Un byte[], jamais une chaîne, même quand les données sont du texte
Un payload invalide Une FormatException, emballée pour vous dans une MethodInvocationException

Un avertissement avant d'écrire le moindre traitement d'erreur : cette FormatException a un message unique qui couvre trois péchés différents. Un caractère hors de l'alphabet, plus de deux caractères de remplissage, ou un caractère non blanc qui se cache parmi le remplissage produisent exactement la même phrase. Quand vous la voyez, le message ne vous dira pas lequel vous avez commis, alors retournez relire votre entrée :

try {
  [System.Convert]::FromBase64String("SGV!G8s=")
}
catch {
  $real = $_.Exception.InnerException
  $real.GetType().Name
  # FormatException
  $real.Message
}

Et la règle du multiple de quatre a un bord qui surprend la première fois qu'on y tombe. Quatre caractères sans aucun remplissage, c'est parfaitement valide ; ça veut juste dire que les bits de réserve du dernier caractère sont jetés. Trois caractères n'est pas un multiple de quatre, et est rejeté :

[System.Convert]::FromBase64String("SGVs").Count
# 3 : quatre caractères sans remplissage, c'est bon
[System.Convert]::FromBase64String("SGV")
# FormatException : trois caractères, pas un multiple de quatre

Ce que le décodeur accepte et ce qu'il refuse

Le décodeur est strict sur l'alphabet et généreux sur une chose précise. Les caractères valides sont les 64 caractères de l'alphabet Base64 (A à Z, a à z, 0 à 9, le plus et le slash) et le signe equals comme remplissage de fin. Exactement quatre caractères blancs sont ignorés où qu'ils apparaissent et aussi souvent qu'ils le font : la tabulation, la fin de ligne, le retour chariot et l'espace. La documentation officielle de .NET les liste par leurs noms Unicode, ce qui vous dit que c'est une garantie documentée, pas un coup de chance.

En pratique c'est un super-pouvoir. MIME, l'encodage du courrier qui a fait connaître le Base64, casse les lignes encodées à 76 caractères, donc un payload qui a voyagé par e-mail, par ticket ou par fichier de log arrive généralement brisé sur de nombreuses lignes. Le décodeur s'en fiche. Collez-le tel quel :

$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. Le texte Base64 arrive
# enveloppé à 76 colonnes dans le mail, donc le décodeur n'a pas à s'en soucier.

Tout ce qui n'est pas un caractère de l'alphabet est un arrêt net. Les fauteurs de troubles les plus courants dans la nature sont l'espace insécable (le favori du texte collé depuis les pages web) et le byte-order mark (la marque invisible qui vous suit quand un fichier a été lu avec le mauvais encodage). Ni l'un ni l'autre n'est un caractère blanc aux yeux de cette méthode, donc les deux lèvent une exception :

try {
  [System.Convert]::FromBase64String("SGVs`u{00A0}G8=")
}
catch {
  $_.Exception.InnerException.GetType().Name
  # FormatException
}

Cette rigueur est volontaire, pas un caprice. La RFC 4648, la norme qui a codifié le Base64 en 2006, dit que les implémentations doivent rejeter les caractères hors de l'alphabet sauf si le protocole autorise explicitement la tolérance, parce qu'un décodeur qui avale silencieusement des caractères étrangers peut être transformé en canal caché pour faire passer en contrebande des données devant tout ce qui n'inspecte que l'alphabet. Le décodeur .NET suit la règle stricte, et vous voulez d'habitude qu'il le fasse.

Un tableau d'octets n'est pas une chaîne

La méthode s'arrête volontairement au tableau d'octets. Ce que ces octets signifient est une seconde décision que seul vous pouvez prendre, et se tromper dessus est l'erreur la plus célèbre du travail Base64 dans PowerShell. L'hypothèse par défaut, UTF-8, est la bonne pour presque tout sur internet, et l'aller-retour fait deux appels :

$bytes = [System.Convert]::FromBase64String("SGVsbG8sIFdvcmxkIQ==")
[System.Text.Encoding]::UTF8.GetString($bytes)
# Hello, World!

Les encodages que vous utiliserez vraiment, et ce que chacun fait quand vous vous trompez :

Encodage À utiliser quand Si vous vous trompez
UTF8 API web, JSON, JWT, le monde moderne. Le défaut sûr Un texte Latin-1 ou UTF-16 revient en charabia
Unicode (UTF-16LE) Le payload vient d'outils Windows, d'une valeur de registre, ou d'une chaîne .NET encodée avant expédition Chaque caractère se retrouve avec un espace autour, parce que vous lisez un octet là où il en fallait deux
ASCII Les identifiants HTTP Basic classiques et autres protocoles garantis 7 bits Tout ce qui dépasse la valeur 127 devient un point d'interrogation
Latin1 Le texte européen legacy qui précède UTF-8 Les suites UTF-8 multi-octets se scindent en plusieurs lettres fausses
Default Presque jamais. C'est la page de code système de la machine Votre script se comporte différemment selon chaque configuration régionale Windows

L'échec classique est du texte UTF-8 décodé en UTF-16. Les octets sont réels, la méthode est contente, et le résultat est quand même du n'importe quoi :

# "SGk=" est les octets UTF-8 de "Hi"
[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String("SGk="))
# Un seul caractère illisible : 2 octets UTF-8 lus comme une unité UTF-16 de 2 octets

La règle pratique : si le texte décodé donne l'impression que chaque caractère a un espace invisible autour, ou qu'il est écrit dans un autre alphabet, vous êtes à un encodage près. Demandez où les données ont été produites, et en cas de doute, faites confiance à UTF-8, mais vérifiez de vos propres yeux les premiers caractères. Et décidez de l'encodage avant de décoder, pas après que le charabia est apparu dans votre log.

base64url : l'alphabet qui se comporte bien dans les URL

Vous croiserez un cousin du Base64 dans chaque jeton d'API, chaque JWT et chaque identifiant enchâssé dans une URL que vous toucherez un jour. Le plus et le slash du Base64 standard ne sont légaux dans une URL qu'après encodage en pourcentage, et le remplissage equals ressemble à un séparateur de champs. La RFC 4648 a donc défini un alphabet sûr pour les URL et les noms de fichiers : les mêmes 64 caractères, sauf que le plus devient un tiret et le slash un underscore. Le remplissage est d'habitude supprimé entièrement, parce que la longueur des données le rend inutile. La RFC prend soin de dire que cette variante doit s'appeler base64url et pas simplement « base64 », et le reste de cette section suit ça.

.NET fournit bel et bien une classe dédiée pour ça, System.Buffers.Text.Base64Url, ajoutée dans .NET 9 avec des méthodes d'encodage et de décodage rapides construites entièrement autour de paramètres ReadOnlySpan<T>. Le PowerShell actuel (7.4 et plus, une fois qu'il tourne sur une version de .NET qui embarque la classe) peut en fait appeler ces surcharges qui prennent des spans directement aujourd'hui, grâce à la conversion implicite tableau/chaîne-vers-span que le liage de méthodes effectue maintenant, donc [System.Buffers.Text.Base64Url]::DecodeFromChars("--__AQI") marche sans cérémonie. Ce n'était pas toujours le cas : Windows PowerShell 5.1 et les anciennes sorties PowerShell 7.x ne pouvaient tout simplement pas lier des paramètres span, et la classe n'existait pas avant .NET 9 pour commencer, donc tout script qui doit tourner sur 5.1, un 7.x plus ancien, ou un hôte antérieur à .NET 9 a encore besoin de la version portable : échanger les deux caractères, et rétablir le remplissage avant de remettre le texte au décodeur standard. Le remplissage à ajouter est ce qui rend la longueur un multiple de quatre :

$token = "--__AQI"  # base64url, sans remplissage
$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

Deux pièges vivent dans ce petit bloc. D'abord, l'arithmétique du remplissage : un payload dont la longueur est déjà un multiple de quatre n'a besoin d'aucun remplissage, et la garde -eq 4 est ce qui garde l'expression honnête. Ensuite, la direction : quand vous ne faites que décoder, vous ajoutez le remplissage et vous échangez les caractères ; vous ne retirez jamais le remplissage d'une entrée Base64 standard, parce que les décodeurs standards s'attendent à le trouver là. Si la source est un JWT ou un jeton d'API, ce sera du base64url sans remplissage, et la recette ci-dessus est exactement la forme que vous voulez.

Ouvrir un JWT sans les clés

Un JSON Web Token est trois segments base64url reliés par des points : l'en-tête, le payload et la signature. Les deux premiers sont du JSON pur, et le Base64 n'est pas un chiffrement, donc n'importe qui qui a le jeton peut les lire tous les deux. C'est une fonctionnalité, pas un défaut : le jeton est conçu pour être inspecté, et c'est la signature qui le rend infalsifiable. PowerShell réduit le coup d'œil à trois lignes :

$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
# propriété name :
(Decode-UrlSegment $parts[1] | ConvertFrom-Json).name
# John Doe

Trois choses à garder en tête. Le troisième segment, la signature, est aussi du base64url, mais il se décode en octets binaires de signature, pas en texte, alors ne vous attendez pas à du joli JSON là. L'en-tête dit d'habitude juste quel algorithme a signé le jeton (HS256, RS256, ...), et un en-tête qui dit none est un drapeau rouge, pas une commodité. Et lire le payload, ce n'est pas lui faire confiance : le base64 vous laisse voir les claims, seule la signature les rend authentiques. Si votre boulot est d'accepter des jetons, vérifiez la signature avec la clé de l'émetteur ; si votre boulot est d'en déboguer un, le code ci-dessus est tout ce qu'il vous faut.

Fichiers, PEM et le long chemin vers les octets

La forme de fichier la plus courante est un fichier texte qui garde le Base64 de quelque chose de plus gros : un blob de sauvegarde, un binaire téléchargé, un objet sérialisé. L'aller-retour fait quatre lignes, et la façon moderne de lire la sortie est un vrai tableau d'octets, pas une supposition en texte :

$encoded = Get-Content -Path ./payload.b64 -Raw
$encoded = $encoded.Trim()
$bytes = [System.Convert]::FromBase64String($encoded)
[System.IO.File]::WriteAllBytes("./payload.bin", $bytes)
$bytes.Length
# combien d'octets portait le texte

Relire le binaire d'origine, c'est là que PowerShell 6 et plus gagnent leur vie. Le paramètre -AsByteStream lit les octets bruts, et avec -Raw il vous remet un vrai byte[] d'un seul coup :

$bytes = Get-Content -Path ./photo.png -AsByteStream -Raw
$encoded = [System.Convert]::ToBase64String($bytes)
Set-Content -Path ./photo.b64 -Value $encoded -NoNewline
$bytes.Length
# la taille originale, avant la taxe texte de 33 pour cent

Laissez tomber -Raw et vous obtenez un flux d'objets octets individuels (un Object[] quand on le capture), ce qui passe pour l'inspection mais est faux pour passer aux méthodes .NET qui attendent un tableau. Et Windows PowerShell 5.1 n'a pas de -AsByteStream du tout, donc sur 5.1 la lecture fiable est [System.IO.File]::ReadAllBytes(), qui existe partout.

PEM est le cousin en armure que vous connaissez de tous les certificats et de toutes les clés privées : un corps Base64 standard, d'habitude enveloppé à 64 caractères, entre des lignes -----BEGIN ... et -----END .... L'armure est du texte ; le corps est le payload. Retirez l'armure, rejoignez les lignes, décodez :

$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
# la taille binaire DER du certificat

Comme le décodeur standard ignore les caractères blancs de toute façon, le -join "" est une sécurité en plus plutôt qu'une exigence, mais rendre le script explicite sur ce qu'il retire fait qu'il se comporte pareil sur chaque machine et avec chaque convention de fin de ligne. L'autre direction, envelopper des octets DER en PEM, c'est juste l'encodeur Base64 plus deux lignes de texte, et l'article sur l'encodage du site sœur montre l'enveloppement à 64 colonnes en entier.

Certificats et la boîte à outils Windows

Les certificats sont les citoyens Base64 les plus lourds du travail quotidien, et PowerShell peut porter toute la famille. Un fichier PFX est un bundle binaire de certificat plus clé privée, et c'est le format que vous trouvez le plus souvent traînant en texte Base64 dans les fichiers de config et les scripts de déploiement. Le décoder de nouveau en certificat vivant est un one-liner avec le type .NET, et ça marche cross-platform sur 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
# quand il cesse d'être vrai

PowerShell 7 fournit aussi Get-PfxCertificate, qui lit un fichier PFX directement depuis le disque avec un paramètre -Password, donc pour les fichiers sur disque vous pouvez sauter le décodage manuel entièrement. Un certificat nu (sans clé) est encore plus simple : les octets DER vont directement dans le même type X509Certificate2 sans aucun mot de passe.

En dehors du langage, deux outils natifs méritent d'être connus. Sur Windows, certutil -decode infile.b64 outfile décode un fichier Base64 avec des sémantiques fichier-entrée/fichier-sortie (ajoutez -f pour écraser), ce qui en fait le réflexe pour les correctifs rapides dans une simple invite de commandes. Son frère certutil -encode a un drapeau qui mérite d'être retenu : -unicodetext convertit le texte d'entrée en UTF-16 avant de l'encoder en Base64, cachant une décision d'encodage entière dans un seul interrupteur. Sur Linux et macOS l'utilitaire classique est base64 -d, qui décode un fichier ou l'entrée standard, en sautant les sauts de ligne par défaut ; sur GNU coreutils ajoutez -i si le payload porte aussi des espaces, des tabulations ou des CRLF venus du courrier Windows.

Des commandes dans une enveloppe Base64

PowerShell a une raison intégrée de parler Base64 depuis la version 1.0 : le paramètre -EncodedCommand de l'hôte lui-même. Vous donnez une chaîne Base64 à pwsh, il décode les octets en UTF-16LE, et le résultat est exécuté en tant que commande. Le but officiel, tout droit sorti de la documentation, c'est de soumettre des commandes qui exigent des guillemets ou des accolades complexes sans se battre avec les règles de guillemets de la shell extérieure :

$command = "Write-Host encoded-hello"
$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command))
# VwByAGkAdABlAC0ASABvAHMAdAAgAGUAbgBjAG8AZABlAGQALQBoAGUAbABsAG8A
pwsh -NoProfile -EncodedCommand $encoded
# encoded-hello

Lisez cette deuxième ligne attentivement, parce que c'est là que tout le monde trébuche : le payload doit être en UTF-16LE, et c'est [System.Text.Encoding]::Unicode. Si vous encodez la commande en UTF-8 à la place, PowerShell la décodera joyeusement en UTF-16LE et exécutera une commande faite de charabia, et le message d'erreur qu'il produira est un portrait parfait de l'erreur :

$wrong = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($command))
pwsh -NoProfile -EncodedCommand $wrong
# Erreur : un mur de caractères illisibles, "The term ... is not recognized..."

Le même mécanisme est la raison pour laquelle les équipes sécurité se soucient du Base64 dans PowerShell. Un long jeton opaque passé à -EncodedCommand est une forme courante d'outillage automatisé, et c'est précisément pour ça que les produits de protection des points de terminaison décodent ces payloads avant qu'ils s'exécutent : rien dans le Base64 ne cache la commande à un décodeur, il ne la cache qu'à un humain qui lit une liste de processus. Si vous générez des commandes encodées pour votre propre automatisation, gardez la commande source à côté du jeton, parce que le jeton ne va pas s'expliquer de lui-même à 3 h du matin.

Décoder quand l'entrée est énorme

Pour les tailles du quotidien, l'approche à méthode unique est la plus rapide. Un binaire de 5 mégaoctets devient une chaîne d'environ 6,9 millions de caractères, et le décodage de cette chaîne ne prend que moins de dix millisecondes sur une machine moderne. La note de la documentation .NET elle-même est que FromBase64String est conçue pour traiter une seule chaîne contenant toutes les données, ce qui est vrai, et qui passe très bien jusqu'à de très grandes limites, parce que la méthode travaille sur la chaîne sur place sans copies supplémentaires significatives.

Quand le payload est plus gros que ce que vous aimeriez garder dans une seule chaîne, ou qu'il arrive en flux (un téléchargement, un socket, un log énorme), l'outil documenté est System.Security.Cryptography.FromBase64Transform emballé dans un CryptoStream : vous lui donnez du texte Base64, vous lisez des octets décodés à la sortie, et seul un petit tampon est vivant à un instant donné. Notez que TransformStream, l'assistant C# pour ça, est une méthode d'extension, et PowerShell ne voit pas les méthodes d'extension, donc vous instanciez le CryptoStream directement :

$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()

Pour les 90 pour cent des boulots, le chemin simple est encore le bon : lisez le fichier texte entier avec Get-Content -Raw, taillez-le, décodez-le, écrivez les octets. Tendez la main vers la version en flux quand le fichier est trop gros pour tenir confortablement en mémoire, ou quand les données arrivent morceau par morceau. Et n'essayez pas de boucler sur les lignes et de décoder chaque ligne séparément : les groupes de quatre caractères du Base64 ne respectent pas vos coupures de ligne, donc une ligne qui coupe un groupe en plein milieu ne se décodera pas toute seule. Lisez tout le texte, puis décodez une fois.

Les pièges qui coûtent des après-midis

  • Le pari du charset. Lire du UTF-8 en UTF-16, ou du Latin-1 en UTF-8, produit un charabia sûr de lui. Décidez de l'encodage d'après la source des données, partez sur UTF-8 par défaut, et regardez les premiers caractères décodés avant de faire confiance au reste.
  • Les caractères invisibles venus du web. Un espace insécable ou un byte-order mark collé depuis une page ou un e-mail riche est un caractère étranger pour le décodeur et lève la FormatException générique. Faites passer l'entrée par .Trim() et une vérification des caractères non imprimables avant de décoder.
  • La confusion du remplissage. Le Base64 standard arrive avec = ou == à la fin ; le base64url des jetons arrive sans. Nourrir l'un avec la recette faite pour l'autre est la casse silencieuse la plus courante du travail d'API, et la vérification de longueur de la section base64url est la protection.
  • Un seul message, trois crimes. Parce que le message FormatException couvre les mauvais caractères, le remplissage excédentaire et le remplissage sale tout d'un coup, les blocs catch qui ne font que journaliser le message vous envoient en rond. Journalisez aussi la longueur de l'entrée et la première région fautive.
  • Attendre une chaîne en retour. Le résultat est toujours un tableau d'octets. Le moment où vous commencez à le formater en chaîne directement, vous obtenez une liste de nombres, pas du texte. Convertissez avec un encodage explicite, une fois, à la fin.
  • La lecture fichier par défaut de 5.1. Windows PowerShell 5.1 lit les fichiers sans BOM avec la page de code ANSI du système, tandis que PowerShell 7 suppose UTF-8. Si votre script lit le fichier texte Base64 sur 5.1 et que le fichier est en UTF-8 avec du non-ASCII autour du payload, la corruption a lieu avant que le décodeur ne le voie jamais.
  • Traiter le Base64 comme un cadenas. C'est une traduction. Un mot de passe, un jeton ou un secret en Base64 est du texte brut qui porte un déguisement, et chaque décodeur de la planète, y compris cet article, l'ouvre en une ligne.

Des habitudes qui gardent les scripts honnêtes

  • Taillez l'entrée externe avant de décoder. Un seul .Trim() élimine plus d'incidents de production que n'importe quel gestionnaire d'erreurs.
  • Validez avant de décoder quand la source est non fiable : après avoir retiré les quatre caractères blancs autorisés, la chaîne ne devrait correspondre qu'à des caractères de l'alphabet avec au plus deux signes equals en fin. Un rapide check regex transforme une exception mystérieuse en message propre d'entrée rejetée.
  • Gardez les octets en octets jusqu'à la toute dernière étape. Décodez une fois, remettez le byte[] à l'API fichier ou à l'encodeur qui en a besoin, et seulement alors convertissez en texte avec un encodage délibéré.
  • Journalisez les longueurs, pas les payloads. La taille de l'entrée et la taille de la sortie décodée vous disent presque tout sur une défaillance de décodage, sans coller des données potentiellement sensibles dans le log.
  • Pour tout ce qui traverse le réseau, notez dans quel alphabet c'est, standard ou base64url, et quelle convention de remplissage, dans la même ligne de code qui le décode. Vous du futur est le consommateur de cette note.

Comment PowerShell a hérité de son décodeur

La plus courte histoire vraie du Base64 dans PowerShell, c'est que PowerShell n'en a jamais écrit un. La méthode que vous utilisez, Convert.FromBase64String, est sortie avec .NET Framework 1.1 en 2003, et chaque PowerShell depuis la version 1.0 en novembre 2006 a simplement exposé le .NET sur lequel il tourne. Le projet s'appelait Monad pendant sa construction, a été montré au public pour la première fois à la Professional Developers Conference en octobre 2003, et au moment de sa sortie, la paire encodeur-décodeur .NET qu'il enveloppe avait déjà trois ans et était en usage quotidien.

Le format lui-même a été standardisé la même année que le lancement de la shell. La RFC 4648, publiée en octobre 2006, est le document qui a fixé l'alphabet, les règles de remplissage, l'attente de décodage strict et la variante base64url, et elle décrit encore exactement le comportement que FromBase64String implémente aujourd'hui. Quand PowerShell est devenu open-source et cross-platform en août 2016 sous le nom de PowerShell Core, le décodeur est venu avec, sans changement, sur Linux et macOS, parce qu'il n'y avait rien à changer.

Le seul vrai ajout, c'est le module Microsoft.PowerShell.TextUtility maintenu par la communauté, disponible sur le PowerShell Gallery, dont le cmdlet ConvertFrom-Base64 enveloppe la même méthode .NET et ajoute un switch -AsByteArray plus un défaut texte qui décode en UTF-8. Installez-le avec Install-Module -Name Microsoft.PowerShell.TextUtility si vous préférez la forme cmdlet, mais une mise en garde : le module est maintenant archivé et plus activement maintenu, et c'est une raison de plus pour que la méthode intégrée reste la recommandation pour les nouveaux scripts.

Des faits qui méritent d'être retenus

  • Le décodeur ignore les tabulations, fins de ligne, retours chariot et espaces n'importe où dans l'entrée. Cent lignes enveloppées se décodent exactement comme une seule longue ligne.
  • $null et la chaîne vide se décodent tous les deux en tableau vide sans se plaindre, ce qui rend FromBase64String inhabituellement tolérant aux bords.
  • Le message unique FormatException couvre trois modes de défaillance différents. Quand il se déclenche, la réponse est dans l'entrée, pas dans le message.
  • "SABpAA==" est la chaîne Hi dans l'encodage interne de PowerShell lui-même, UTF-16LE. Elle est deux fois plus longue que l'encodage UTF-8 des mêmes deux lettres, et ce ratio est l'empreinte digitale du texte natif Windows dans n'importe quel Base64 que vous lirez.
  • -EncodedCommand existe depuis la toute première sortie de PowerShell, et son payload est spécifié en UTF-16LE, pas UTF-8. Utilisez le mauvais encodage et la shell exécutera joyeusement votre charabia.
  • Les aides Base64 plus récentes de .NET basées sur les spans, y compris la classe Base64Url, étaient inatteignables depuis les anciennes sorties de PowerShell, parce que les spans sont des types du genre byref que le liage de méthodes ne pouvait pas lier. Ça a changé : le PowerShell actuel (7.4 et plus, sur une version de .NET assez neuve pour embarquer la classe) résout un argument tableau ou chaîne contre un paramètre ReadOnlySpan<T> sans se plaindre, donc l'appel direct marche aujourd'hui. L'échange de deux caractères mérite encore sa place comme la version qui tourne aussi sur Windows PowerShell 5.1 et les hôtes plus anciens, pas comme le seul chemin restant.
  • Get-Content -AsByteStream sans -Raw vous donne un flux d'objets octets, pas un tableau d'octets. Ajoutez -Raw et le type est exactement ce que les méthodes .NET attendent.

Le grand détour

Tout dans cet article est question de prendre une chaîne Base64 et de récupérer vos données. L'opération miroir, transformer des données en Base64, a l'air d'un one-liner jusqu'à ce que vous tombiez sur le fait que les chaînes de PowerShell ne sont pas des octets, que UTF-16 double votre taille, que l'enveloppement de lignes a deux largeurs conventionnelles, et que la sortie base64url a besoin de sa propre chirurgie à deux caractères. Cette direction a droit à son propre traitement complet, avec ses propres pièges et sa propre histoire, dans l'article lié sur le site sœur, l'encodage Base64 dans PowerShell, en lien ci-dessous.

Dernière mise à jour : 2026-09-07

Article associé : Encodage Base64 en PowerShell : un guide complet