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.

Encodage Base64 en PowerShell : un guide complet

Vous avez une chaîne, un fichier, un certificat ou un jeton, et l'autre bout du fil le veut sous forme d'une longue suite de lettres et de chiffres : imprimable, collable dans un e-mail, une URL ou un fichier de config, sans le moindre octet binaire pour casser le transport. C'est le Base64. C'est une traduction, pas une compression et pas un cadenas : trois octets en entrée deviennent quatre caractères en sortie, donc le texte que vous expédiez fait environ 33 % de plus que ce qui l'a commencé, 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. Cet article couvre la direction encodage côté PowerShell : la seule méthode .NET que vous appellerez, l'étape manquante qui fait trébucher tout le monde sur son premier script, les conventions d'enveloppement de lignes qui diffèrent selon le protocole, l'alphabet sûr pour les URL, et la poignée de boulots réels où encoder dans PowerShell récompense le prudent et punit le négligent.

La méthode et l'étape manquante

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

$bytes = [System.Text.Encoding]::UTF8.GetBytes("Hello")
[System.Convert]::ToBase64String($bytes)
# SGVsbG8=

C'est toute l'API : un tableau d'octets en entrée, une chaîne en sortie, sur chaque PowerShell de chaque système d'exploitation, parce que c'est simplement du .NET. L'étape manquante est la première ligne de l'exemple, et c'est là que les débutants perdent leur première heure. La méthode n'accepte pas votre chaîne. Elle accepte des octets, et la question « quels octets signifie ma chaîne » est une question d'encodage à laquelle seul vous pouvez répondre. Voici le contrat des surcharges que vous pouvez en fait appeler depuis PowerShell :

Ce que vous passez Ce que vous obtenez
byte[] Une longue ligne de Base64 standard, avec du remplissage = quand la longueur l'exige
byte[] plus InsertLineBreaks Les mêmes données, coupées à 76 caractères avec CRLF entre les lignes
byte[], offset, count Uniquement la tranche demandée du tableau, encodée
Une chaîne comme "Hello" Une exception de conversion. PowerShell ne peut pas transformer lui-même une chaîne en tableau d'octets
$null Une ArgumentNullException, emballée pour vous dans une MethodInvocationException

Remarquez ce qui n'est pas dans ce tableau : il n'y a pas de surcharge qui dit « encoder ce texte ». Encoder du texte dans PowerShell est toujours un processus en deux étapes. Vous décidez de l'encodage, vous produisez les octets, et seulement alors la méthode Base64 entre dans la conversation. Gardez ces deux décisions visiblement séparées dans le script, parce que la seconde est invisible et c'est la première où vivent les bugs.

Encoder du texte : choisir l'encodage en premier

Le défaut sûr pour tout ce qui traverse l'internet moderne est UTF-8. API web, JSON, JWT, tout ce qu'un navigateur ou un serveur a écrit ces dix dernières années s'attend à des octets UTF-8 sous le Base64, et le schéma en deux étapes est l'habitude que vous voulez construire :

$text = "Hello, PowerShell!"
$bytes = [System.Text.Encoding]::UTF8.GetBytes($text)
$encoded = [System.Convert]::ToBase64String($bytes)
# SGVsbG8sIFBvd2VyU2hlbGwh

Quand vous tendez la main vers un autre encodage, c'est d'habitude pour servir un système legacy, et le tableau ci-dessous est le guide pratique :

Encodage À utiliser quand Si vous choisissez le mauvais
UTF8 API web, JSON, JWT, le monde moderne. Le choix par défaut Le décodeur de l'autre côté voit du charabia au lieu de votre texte
Unicode (UTF-16LE) Le consommateur est un composant Windows ou .NET qui encode des chaînes .NET, ou -EncodedCommand Votre payload est deux fois plus long que le consommateur ne l'attend, et plein de surprises
ASCII Les protocoles 7 bits classiques comme les identifiants HTTP Basic Tout ce qui dépasse la valeur 127 est remplacé avant même que l'encodage n'ait lieu
Latin1 Les systèmes européens legacy qui précèdent UTF-8 Un octet par caractère, et chaque caractère non Latin-1 devient un point d'interrogation

Une astuce de débogage utile qui marche dans les deux sens : le remplissage et la longueur du Base64 vous disent combien d'octets ont été encodés, et la façon dont le texte décodé se présente vous dit de quel monde deux-octets ou un-octet il vient. Un payload d'une taille suspectement paire, où des caractères normaux et des caractères qui semblent vides alternent, est d'habitude du UTF-16 qui porte un déguisement UTF-8, ou l'inverse.

La surprise UTF-16

PowerShell stocke les chaînes en UTF-16 en interne, et ça fuit jusque dans le travail Base64 à un endroit précis et très commun : vous écrivez le Base64 pour un consommateur qui est lui-même un composant .NET ou Windows, et vous encodez avec Unicode parce que c'est comme ça que sont les chaînes .NET. C'est un instinct juste pour certains consommateurs et une erreur de doublement de taille pour tous les autres. Les mêmes quatre caractères visibles, deux encodages :

$same = "Café"
[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($same))
# Q2Fmw6k=  cinq octets
[System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($same))
# QwBhAGYA6QA=  huit octets

Le même texte, le double de la taille, et les deux chaînes ne sont pas interchangeables : un consommateur qui s'attend à l'une et reçoit l'autre ne va pas échouer à voix haute ; il va juste lire du n'importe quoi. La règle qui empêche ça de vous mordre est de traiter l'encodage comme une partie du protocole, pas un détail local. Si le système récepteur est un navigateur, une API REST ou un serveur moderne, c'est UTF-8 sauf si la documentation dit le contraire. Si c'est l'hôte PowerShell lui-même via -EncodedCommand, ou une chaîne .NET dans un pipeline Windows-only, c'est UTF-16LE. Quand le protocole ne dit rien, demandez à l'autre côté avec quoi il va appeler GetString, parce que c'est la question qui décide vraiment.

Nombres, octets et tout le reste

La méthode est déclarée pour prendre un tableau d'octets, mais la conversion de types de PowerShell est généreuse sur ce qui compte comme un tableau d'octets, et connaître les bords vous épargne des surprises :

[System.Convert]::ToBase64String([byte[]](1, 2, 3, 250, 251))
# AQID+vs=
[System.Convert]::ToBase64String([char[]]"Café")
# Q2Fm6Q==  un octet par caractère, la valeur du caractère en nombre
[System.Convert]::ToBase64String([int[]](72, 101, 108, 108, 111))
# SGVsbG8=
[System.Convert]::ToBase64String(123)
# ew==  un seul nombre est accepté là où un tableau entier est attendu

Deux bords de ce bloc méritent une attention. Le tableau de caractères se convertit en un octet par caractère en utilisant la valeur numérique du caractère, ce qui pour du texte latin est exactement ce que les systèmes legacy qui utilisent cette astuce attendent, et pour tout au-delà, ça produit silencieusement les mauvais octets. Un entier plus grand que 255 est le bord qui échoue à voix haute à la place : la conversion d'octet de PowerShell refuse les valeurs hors de 0-255 avec une exception, donc 256 arrête le script au cast au lieu de corrompre silencieusement vos données. Si votre source est constituée de nombres, faites le cast explicite : [byte[]](1, 2, 3) dit exactement ce qu'il veut dire.

Passez à la méthode une chaîne et vous obtenez le même traitement bruyant pour une autre raison : il n'y a pas de moyen de savoir quels octets signifie une chaîne, alors le moteur de conversion de PowerShell abandonne. Passez-lui $null et .NET lève une exception avant de faire quoi que ce soit. Les deux sont des comportements corrects, et les deux sont la raison pour laquelle le schéma en deux étapes de la première section est le seul schéma qui vaille la peine.

L'enveloppement de lignes : 76, 64 et aucun

Par défaut l'encodeur produit une seule longue ligne, quelle que soit la quantité de données que vous lui donnez. Pour un fichier de quelques kilo-octets, c'est bon. Pour des données qui seront lues par un humain, collées dans un e-mail, ou comparées dans un diff de contrôle de version, un mur de huit millions de caractères est un problème pratique, et la convention est d'envelopper. PowerShell et les protocoles qu'il sert connaissent trois largeurs, et elles ne sont pas interchangeables :

Largeur Qui s'y attend Fin de ligne
76 caractères MIME, le courrier et la plupart des transports texte. Le défaut de InsertLineBreaks CRLF
64 caractères Les fichiers PEM : certificats, clés privées et le reste de la famille -----BEGIN Conventionnellement LF
Aucune APIs, jetons, fichiers de config, tout ce qui est traité par machine Pas de ligne du tout

L'enveloppement intégré est un changement d'un seul paramètre, et c'est celui que vous voulez pour les payloads de style courrier :

$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 caractères par ligne, CRLF entre elles, exactement comme MIME s'y attend

PEM est l'exception à l'intégré, parce qu'OpenSSL et tout l'écosystème -----BEGIN enveloppent à 64 caractères, et aucun drapeau .NET produit cette largeur. La boucle est courte et c'est la recette standard :

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

La raison pour laquelle la largeur compte, c'est que les groupes de quatre caractères du Base64 ne respectent pas les coupures de ligne, donc un décodeur peut ignorer les coupures entièrement ou il peut les imposer. Le décodeur que ce site utilise les ignore, mais les consommateurs stricts, et il y en a beaucoup dans les mondes des certificats et du courrier, traitent une coupure inattendue comme un caractère étranger et rejettent le payload. Quand vous choisissez une largeur, vous faites un contrat avec le consommateur, et ça vaut le commentaire dans le script qui dit avec qui vous contractez.

base64url : deux caractères et une décision de remplissage

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 se lit comme 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, et le remplissage est d'habitude supprimé parce que la longueur des données le rend inutile. Chaque jeton d'API et chaque JWT que vous avez jamais manipulé est écrit dans cette variante, que la norme insiste pour appeler base64url et pas juste base64.

L'encodeur standard de PowerShell produit l'alphabet standard, donc la conversion en base64url est deux échanges de caractères et une décision de remplissage :

$bytes = [System.Text.Encoding]::UTF8.GetBytes("Париж encoded 大阪")
$standard = [System.Convert]::ToBase64String($bytes)
$standard
# 0J/QsNGA0LjQtiBlbmNvZGVkIOWkp+mYqg==  l'alphabet standard, remplissage inclus
$url = $standard.Replace("+", "-").Replace("/", "_").TrimEnd("=")
$url
# 0J_QsNGA0LjQtiBlbmNvZGVkIOWkp-mYqg  la forme sûre pour les URL, remplissage retiré

Supprimer le remplissage est sûr dans le monde base64url, parce que le consommateur recalcule ce que le remplissage aurait été d'après la longueur de la chaîne. Mais ce n'est pas vrai partout, alors prenez la décision explicitement : retirez le remplissage pour les jetons, les segments JWT et l'enchâssement dans une URL, gardez-le pour tout ce qui alimente un consommateur strict de l'alphabet standard, et notez lequel vous avez choisi. Le runtime .NET embarque une classe dédiée pour cet alphabet, System.Buffers.Text.Base64Url (ajoutée dans .NET 9), avec des méthodes construites 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 méthodes directement aujourd'hui - [System.Buffers.Text.Base64Url]::EncodeToString($bytes) marche aujourd'hui, parce que le liage de méthodes convertit maintenant implicitement un argument tableau en span - mais l'échange de deux caractères reste celui vers lequel tendre la main quand le script doit tourner sur Windows PowerShell 5.1, une sortie PowerShell 7.x plus ancienne, ou un hôte sur un runtime antérieur à .NET 9, et il marche sur chacune de ces versions.

La frappe d'un JWT

Un JSON Web Token est l'usage réel phare du base64url, et c'est aussi un beau test complet du pipeline d'encodage, parce qu'un JWT est trois segments encodés reliés par des points : l'en-tête, le payload et la signature. Les deux premiers sont du JSON compact en base64url, et le troisième est la sortie binaire d'un hachage sur le texte exact des deux premiers. Voici un jeton HS256 complet construit dans PowerShell, du début à la fin :

$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
#  sur PowerShell 7.4 ; l'ordre interne des clés des segments - et donc la signature - peut varier selon la version

Regardez le segment de signature : une suite de l'alphabet base64url, celle où le plus apparaîtrait comme tiret et le slash comme underscore. Trois choses sur cet exemple vous éviteront des incidents de production. D'abord, la signature est calculée sur le texte JSON exact, ordre des clés et espacements compris, donc le JSON que vous signez et le JSON contre lequel vous vérifiez doivent être identiques octet pour octet. Le ConvertTo-Json de PowerShell décide de l'ordre des clés pour vous, et c'est quelque chose que vous ne contrôlez pas, alors ne réordonnez pas à la main les segments d'un jeton et ne les reformatez pas entre la signature et la vérification. Ensuite, -Compress n'est pas cosmétique : un jeton dont l'en-tête ou le payload contient un seul espace ne vérifiera jamais contre une implémentation conforme, parce que la forme standard est compacte. Troisièmement, le horodatage iat est des secondes depuis l'époque Unix, et un payload construit depuis Get-Date sans conversion sera hors de portée de plusieurs années. La direction décodage, jeter un œil dans un jeton frappé par quelqu'un d'autre, est couverte dans l'article lié sur le site sœur.

Fichiers et le flux d'octets

Les fichiers sont le payload le plus commun de tous, et le pipeline est court. Lisez le fichier en octets, encodez, écrivez le texte. Les deux lignes qui comptent sont la lecture, qui doit être une lecture d'octets, et l'écriture, qui ne doit d'habitude pas ajouter de saut de ligne final :

$bytes = [System.IO.File]::ReadAllBytes("./photo.png")
$encoded = [System.Convert]::ToBase64String($bytes)
Set-Content -Path "./photo.b64" -Value $encoded -NoNewline
$encoded.Length
# la taille du texte que vous vous apprêtez à expédier

Deux notes pratiques. La première est de l'arithmétique : le Base64 grossit tout, et pour un fichier de 10 mégaoctets le texte que vous expédiez fait environ 13,4 mégaoctets. Si le transport a une limite de taille, ou si ce texte va dans un corps d'e-mail ou une URL, faites le calcul avant d'encoder, pas après l'erreur. La seconde est le saut de ligne final : Set-Content en ajoute un par défaut, et bien que le décodeur utilisé par ce site et la plupart des décodeurs modernes l'ignorent, certains consommateurs stricts ne le font pas. -NoNewline ne vous coûte rien et retire la question.

PowerShell 6 et plus offrent une seconde lecture qui reste dans le langage : Get-Content -AsByteStream -Raw renvoie le fichier en un seul tableau d'octets en un seul appel, ce qui est une alternative propre à ReadAllBytes de .NET et qui se comporte identiquement pour ce usage. Sur Windows PowerShell 5.1, qui n'a pas de -AsByteStream, la lecture .NET est la seule option, et c'est celle qui se comporte pareil sur chaque version de la shell.

Certificats : de PEM et PFX au texte

Les certificats sont les citoyens de l'encodage les plus lourds du quotidien, parce que les déploiements adorent les porter en texte. Un certificat PEM est un corps Base64 enveloppé entre des lignes d'armure, et la recette de la section enveloppement est l'export entier :

$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new([System.IO.File]::ReadAllBytes("./certificate.der"))
$cert.Subject
# CN=example.org
$b64 = [System.Convert]::ToBase64String($cert.RawData)
# une longue ligne de la forme binaire du certificat

Le format PFX est l'autre cheval de trait : un fichier binaire unique qui garde le certificat avec sa clé privée, et c'est pourquoi c'est le format que vous trouvez le plus souvent traînant en texte Base64 à l'intérieur des scripts de déploiement et des stockages de configuration. L'encoder, c'est le pipeline fichier tout simple de la section précédente, et la direction lecture dans PowerShell 7 est une affaire d'un seul cmdlet :

$pfxBytes = [System.IO.File]::ReadAllBytes("./certificate.pfx")
$pfxB64 = [System.Convert]::ToBase64String($pfxBytes)
# la forme texte du bundle, prête pour un fichier de config
Get-PfxCertificate -FilePath "./certificate.pfx" -Password (ConvertTo-SecureString "secret" -AsPlainText -Force)
# le certificat vivant, pas de décodage manuel nécessaire

Une phrase de sécurité, dite franchement parce que le Base64 invite l'hypothèse contraire : un PFX en Base64 est une clé privée en texte. L'encodage change la forme du secret et rien de sa confidentialité, donc un PFX Base64 collé dans une fenêtre de chat, un ticket ou un commit est une clé privée collée dans une fenêtre de chat, un ticket ou un commit. Traitez la forme texte avec exactement le même soin que la forme binaire, et préférez le magasin de certificats ou un gestionnaire de secrets à l'une ou l'autre.

Basic Auth, data URIs et les vieilles habitudes

Le Base64 est plus vieux que le document de norme qui l'a nommé. La famille de RFC MIME de 1996 l'a mis dans l'e-mail, et l'authentification HTTP Basic l'a mis dans chaque échange d'en-têtes du web des débuts, où le client encode toujours la paire d'identifiants en une chaîne Base64 unique :

$credential = [System.Text.Encoding]::UTF8.GetBytes("alice:s3cret!")
[System.Convert]::ToBase64String($credential)
# YWxpY2U6czNjcmV0IQ==
# envoyé comme : Authorization: Basic YWxpY2U6czNjcmV0IQ==

L'alphabet standard est le bon ici, plus et slash compris, parce qu'un en-tête n'est pas une URL et n'a pas besoin de l'alphabet sûr. Le même mécanisme apparaît dans les data URIs, la façon dont un document embarque son propre binaire à l'intérieur, et la forme est un préfixe littéral plus le Base64 standard des octets :

$dataUri = "data:application/octet-stream;base64," + [System.Convert]::ToBase64String([byte[]](1, 2, 3, 250, 251))
# data:application/octet-stream;base64,AQID+vs=

Les deux habitudes méritent d'être connues moins comme des choses que vous allez construire que comme des choses que vous allez croiser : quand un en-tête ou un lien contient une longue suite Base64, ces deux formats sont les premiers à vérifier, et les deux sont à un simple décodage de ce qu'ils disent. C'est le but du format, et la raison pour laquelle le côté décodage de ce site existe.

Commandes encodées et la boîte à outils Windows

PowerShell porte une raison intégrée d'encoder 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 s'exécute en tant que commande. Le but documenté, ce sont des commandes qui se battent avec le guillemetage de la shell extérieure, et le côté encodage fait deux lignes :

$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

Lisez la ligne d'encodage attentivement, parce que c'est celle que tout le monde rate : le payload doit être en UTF-16LE, et c'est l'encodage Unicode, pas UTF-8. Encodagez avec le mauvais et l'hôte décode vos octets en UTF-16LE quand même et exécute une commande faite de charabia, en produisant une erreur qui est un portrait parfait de la faute. L'article sur le décodage couvre cette défaillance en entier, et le correctif de ce côté est un seul mot : Unicode.

En dehors du langage, les outils natifs portent chacun leur propre décision d'encodage silencieuse. Sur Windows, certutil -encode infile outfile.b64 produit un fichier Base64 standard avec les lignes d'armure que PEM attend, -f écrase une sortie existante, et le drapeau qui mérite d'être retenu est -unicodetext, qui fait écrire le fichier de sortie en Unicode par certutil (selon la documentation de Microsoft : « Écrire le fichier de sortie en Unicode ») - un seul interrupteur qui cache une décision d'encodage. Sur Linux l'utilitaire classique est base64 -w 0 file, où le -w 0 est la partie portante : sans lui, base64 GNU enveloppe à 76 caractères et vous remet un fichier de style MIME quand vous vouliez une ligne. Sur macOS la saveur BSD n'a pas besoin d'un tel drapeau, parce qu'elle émet une ligne non coupée par défaut.

Encoder quand la sortie est énorme

Pour les tailles du quotidien, le pipeline lire-tout-encoder-tout est le rapide et le simple, et c'est le bon tant que le fichier n'est pas trop grand pour tenir confortablement en mémoire ou que les données n'arrivent pas morceau par morceau d'un téléchargement ou d'un socket. Alors l'outil documenté est la paire en flux : System.Security.Cryptography.ToBase64Transform emballé dans un CryptoStream, où vous écrivez des octets bruts dedans et du texte Base64 sort, avec seulement un petit tampon vivant à un instant donné :

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

Une différence avec la méthode en un tir mérite d'être notée : le flux produit une ligne continue sans aucun enveloppement, quelle que soit la taille de l'entrée. Un décodeur qui ignore les caractères blancs s'en fiche, mais si la destination finale est un fichier PEM, passez la boucle à 64 colonnes de la section enveloppement sur le résultat après coup. Et en C# la forme standard est le même motif ToBase64Transform + CryptoStream que vous voyez dans l'article C# ; PowerShell le pilote directement, comme ci-dessus.

Où les payloads encodés se gâtent

  • Encoder la chaîne, pas les octets. ToBase64String("Hello") lève une exception de conversion, et c'est la méthode qui vous dit que la première décision, l'encodage, n'a pas été prise. Rendez-le visible dans le script et l'erreur disparaît.
  • UTF-16 là où UTF-8 était promis. Le payload est deux fois plus long que prévu et le consommateur lit du n'importe quoi. L'encodage fait partie du protocole, et pour presque chaque fil de l'internet moderne le protocole dit UTF-8.
  • La lecture de 5.1. Windows PowerShell 5.1 lit un fichier texte sans BOM avec la page de code ANSI de la machine avant que votre script ne le voie, donc un fichier source UTF-8 peut être corrompu avant l'étape d'encodage. Sur 5.1, lisez le texte avec une lecture UTF-8 explicite et vérifiez les premiers caractères du résultat.
  • La mauvaise largeur d'enveloppement. MIME veut 76, PEM veut 64, les APIs veulent aucune, et un consommateur strict traite une coupure de ligne inattendue comme un caractère étranger. Choisissez la largeur d'après le consommateur et dites-le dans un commentaire.
  • Le remplissage du mauvais côté de l'échange. Retirer les signes equals est correct pour les jetons base64url et faux pour un consommateur qui s'attend au remplissage standard. L'échange d'alphabet et la décision de remplissage sont deux choix, pas un.
  • Reformater ce que vous avez signé. Une signature JWT couvre le texte JSON exact, ordre des clés et espacements compris. Réordonnez les claims ou ajoutez un espace et le jeton cesse de vérifier, sans message d'erreur nulle part près de la cause.
  • Les valeurs au-delà de 255. Convertir un entier en octet lève au lieu de s'enrouler, donc 256 arrête le script au cast. Si vos données source sont des nombres, cast explicitement et laissez une erreur être une erreur que vous voyez.
  • Croire le déguisement. Le Base64 n'est ni un chiffrement ni une compression : c'est une traduction qui grossit les données d'un tiers. Un secret en Base64 est un secret en texte brut, et un fichier en Base64 est un fichier qui a besoin de 33 % de plus de place.

Des règles pour des encodeurs dignes de confiance

  • Produisez les octets délibérément. La première ligne de n'importe quel script d'encodage devrait être un GetBytes explicite ou une lecture d'octets, jamais un espoir que PowerShell transformera une chaîne en les bons octets.
  • Nommez l'alphabet et la largeur dans un commentaire à côté du code qui fait le choix : standard ou base64url, enveloppé à 76, 64 ou pas du tout. Le consommateur est une personne qui lit le script dans six mois, et cette personne c'est vous.
  • Écrivez le fichier texte avec -NoNewline sauf si le consommateur s'attend spécifiquement à une coupure finale, et choisissez la fin de ligne (LF ou CRLF) comme la documentation du consommateur l'attend.
  • Testez l'aller-retour pendant que vous construisez : encodez, décodez, comparez les octets. Un Compare-Object de trente secondes sur les deux tableaux d'octets attrape les erreurs d'encodage, les erreurs d'enveloppement et les erreurs d'ordre des octets tout d'un coup, pendant que la cause est encore fraîche.
  • Journalisez les tailles, pas les payloads. Le nombre d'octets avant et le nombre de caractères après devraient tenir à un ratio d'environ 1,33, et quand ce n'est pas le cas, le désaccord de taille vous dit où regarder sans que le log contienne jamais les données.

Comment PowerShell a hérité de son encodeur

La plus courte histoire vraie de l'encodage Base64 dans PowerShell, c'est que PowerShell n'en a jamais écrit un. La méthode que vous appelez, Convert.ToBase64String, 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 la sortie, l'encodeur .NET qu'il enveloppait avait déjà trois ans et portait du trafic web.

Le format a été standardisé la même année que le lancement de la shell. La RFC 4648, publiée en octobre 2006, a fixé l'alphabet, les règles de remplissage, la rigueur du décodage et la variante base64url, et elle décrit encore exactement le comportement que la paire .NET implémente. Les RFC MIME qui l'ont précédée, en 1996, avaient déjà mis l'enveloppement à 76 caractères dans l'e-mail, et c'est pourquoi cette largeur est le défaut de InsertLineBreaks à ce jour. Quand PowerShell est devenu open-source et cross-platform en août 2016 sous le nom de PowerShell Core, l'encodeur est venu avec sur Linux et macOS sans changement, parce qu'il n'y avait rien à changer.

Ce qui a changé plus tard est arrivé dans .NET, et surtout hors de la portée de PowerShell. Le runtime a gagné des aides Base64 plus rapides basées sur les spans dans les versions récentes, y compris la classe Base64Url et les méthodes de décodage préfixées par Try. Les spans sont des types du genre byref, et les anciennes sorties de PowerShell ne pouvaient vraiment pas lier dessus du tout, mais le liage de méthodes du PowerShell actuel effectue maintenant une conversion implicite tableau-vers-span, donc ces raccourcis sont appelables depuis un script sur un hôte assez récent. La réponse de la communauté pour tout ce qui est plus ancien, c'est le module Microsoft.PowerShell.TextUtility du PowerShell Gallery, dont ConvertTo-Base64 enveloppe la même méthode .NET et ajoute un paramètre -Text avec un défaut UTF-8 et un switch -InsertBreakLines pour l'enveloppement à 76 colonnes. Installez-le avec Install-Module -Name Microsoft.PowerShell.TextUtility si vous préférez la forme cmdlet, et notez que le module est archivé et plus activement maintenu, ce qui est une raison de plus pour que la méthode intégrée reste la recommandation pour les nouveaux scripts.

Les nombres et les noms à retenir

  • Chaque trois octets en entrée deviennent quatre caractères en sortie, donc les données encodées font environ 33 % de plus que l'original, et aucun remplissage ne fait jamais plus de deux signes equals.
  • La sortie par défaut est une ligne non coupée. InsertLineBreaks enveloppe à 76 caractères avec CRLF, la convention MIME de 1996. PEM veut 64, et aucun drapeau intégré produit cette largeur.
  • Le base64url est du Base64 standard avec le plus et le slash échangés contre tiret et underscore, le remplissage d'habitude supprimé, et c'est l'alphabet de chaque JWT et jeton d'API.
  • « Café » fait cinq octets en UTF-8 et huit en UTF-16LE. Le même texte visible, le double de la taille, et les deux encodages ne sont pas interchangeables sur le fil.
  • -EncodedCommand existe depuis la toute première sortie de PowerShell, et son payload doit être UTF-16LE, pas UTF-8. Le mot unique qui corrige l'erreur la plus courante de ce côté est Unicode.
  • certutil -encode peut cacher une décision d'encodage dans -unicodetext, et base64 GNU a besoin de -w 0 pour vous donner une ligne au lieu de l'enveloppement à 76 colonnes.
  • Les aides Base64 de .NET basées sur les spans, y compris Base64Url, étaient un temps inatteignables depuis PowerShell parce que les spans sont des types du genre byref que l'ancien liage de méthodes ne pouvait pas lier. Le PowerShell actuel (7.4 et plus, sur un runtime .NET assez neuf pour embarquer la classe) résout un argument tableau contre un paramètre span sans se plaindre, donc l'appel direct marche aujourd'hui - mais l'échange de deux caractères reste la seule recette qui marche sur chaque version, ancienne et neuve.
  • Un octet unique, 123, s'encode en ew== : le plus petit exemple possible de la règle que la longueur de la sortie vous dit la longueur de l'entrée.

Renverser la flèche

Tout dans cet article est question de prendre des données que vous tenez et de les transformer en chaîne Base64. L'opération miroir, prendre une chaîne et récupérer vos données, a elle aussi sa distribution de problèmes : un décodeur qui ignore quatre sortes de caractères blancs, un message d'erreur couvrant trois crimes, un JWT dans lequel jeter un œil, un certificat à déballer, et un -EncodedCommand à expliquer. 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, le décodage Base64 dans PowerShell, en lien ci-dessous.

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

Article associé : Décodage Base64 en PowerShell : un guide complet