Você tem que lidar com o formato Base64? Então esse site é perfeito para você! Use o nosso ferramenta online super útil para codificar ou decodificar os seus dados.

Decodificação Base64 em PowerShell: um guia completo

Em alguma linha de log, arquivo de configuração ou mensagem de erro, você se depara com ele: uma longa sequência de letras e dígitos com o ocasional mais ou barra, e um ou dois sinais de igual estacionados de forma suspeita no final. Parece ruído. Não é. É Base64, e você já sabe do que precisa: a coisa que ele está escondendo.

Base64 é uma tradução, não uma compressão e nem um cadeado. Ele reescreve qualquer sequência de bytes como texto imprimível, quatro caracteres para cada três bytes de entrada (por isso os dados codificados ficam cerca de 33% maiores que o original), usando um alfabeto de 64 caracteres mais o sinal de igual como padding no final. A página inicial deste site percorre o alfabeto, as contas de bits e as variantes em detalhe, então este artigo gasta seu tempo onde o PowerShell faz a diferença: o único método .NET que você vai chamar, as regras que ele impõe, e os uns doze cantos do trabalho real onde decodificar no PowerShell fica interessante.

O método e o seu contrato

O PowerShell não vem com nenhum cmdlet próprio de Base64. O trabalho é feito por um método de uma classe .NET que faz parte do framework desde o .NET Framework 1.1 em 2003, três anos antes do próprio PowerShell ser lançado:

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

Essa é a API inteira: uma string entra, um array de bytes sai. Funciona em todo PowerShell em qualquer sistema operacional, no Windows PowerShell 5.1 e no PowerShell 7 em Windows, Linux e macOS, porque ele é simplesmente .NET. O contrato é curto o suficiente para decorar, então aqui está ele em forma de tabela:

Entrada O que você recebe de volta
$null Um array vazio, sem erro. O PowerShell transforma $null em uma string vazia em silêncio antes da chamada
Uma string vazia Um array vazio, sem erro
Um payload válido Um byte[], nunca uma string, mesmo quando os dados são texto
Um payload inválido Um FormatException, embrulhado para você num MethodInvocationException

Um aviso antes de você escrever qualquer tratamento de erro: aquele FormatException tem uma única mensagem que cobre três pecados diferentes. Um caractere fora do alfabeto, mais de dois caracteres de padding, ou um caractere que não é espaço em branco escondido entre o padding produzem exatamente a mesma frase. Quando você a vê, a mensagem não diz qual dos três você cometeu, então você volta e relê sua entrada:

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

E a regra do múltiplo de quatro tem uma aresta que pega todo mundo de surpresa na primeira vez que você bate nela. Quatro caracteres sem nenhum padding são perfeitamente válidos; isso só significa que os bits sobrando no último caractere são descartados. Três caracteres não é múltiplo de quatro, e é rejeitado:

[System.Convert]::FromBase64String("SGVs").Count
# 3: quatro caracteres sem padding está tudo bem
[System.Convert]::FromBase64String("SGV")
# FormatException: três caracteres não é múltiplo de quatro

O que o decodificador aceita e o que ele não aceita

O decodificador é estrito quanto ao alfabeto e generoso quanto a uma coisa específica. Os caracteres válidos são os 64 dígitos do Base64 (A até Z, a até z, 0 até 9, mais e barra) e o sinal de igual como padding no final. Exatamente quatro caracteres de espaço em branco são ignorados onde quer que apareçam e quantas vezes quer que seja: a tabulação, o line feed, o carriage return e o espaço. A documentação oficial do .NET os lista pelos nomes Unicode, o que mostra que isso é uma garantia documentada e não um acidente de sorte.

Na prática, isso é um superpoder. O MIME, a codificação de e-mail que colocou o Base64 no mapa, embrulha as linhas codificadas a cada 76 caracteres, então um payload que viajou por e-mail, por um chamado ou por um arquivo de log geralmente chega partido em muitas linhas. O decodificador não se importa. Cole como está:

$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. O texto Base64 chega
# embrulhado em 76 colunas no e-mail, então o decodificador não precisa se importar.

Tudo o mais que não for caractere do alfabeto é uma parada dura. Os causadores mais comuns no mundo real são o espaço não separável (o favorito do texto colado de páginas da web) e o byte-order mark (a marca invisível que te segue quando um arquivo foi lido com a codificação errada). Nenhum dos dois é espaço em branco para este método, então os dois lançam exceção:

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

A estriteza é de propósito, não é mania. O RFC 4648, o padrão que codificou o Base64 em 2006, diz que as implementações devem rejeitar caracteres fora do alfabeto, a menos que o protocolo permita explicitamente tolerância, porque um decodificador que engole silenciosamente caracteres estrangeiros pode se tornar um canal encoberto para contrabandear dados por cima de qualquer coisa que só inspecione o alfabeto. O decodificador do .NET segue a regra estrita, e a maioria das vezes é assim que você quer.

Um array de bytes não é uma string

O método para de propósito no array de bytes. O que esses bytes significam é uma segunda decisão que só você pode tomar, e errar esse palpite é o erro mais famoso do trabalho com Base64 em PowerShell. A suposição padrão, UTF-8, está certa para quase tudo na internet, e a ida e volta são duas chamadas:

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

As codificações que você realmente vai usar, e o que cada uma faz quando você erra o palpite:

Codificação Use quando Se você errar o palpite
UTF8 Web APIs, JSON, JWTs, tudo o que é moderno. O padrão seguro Texto Latin-1 ou UTF-16 volta como mojibake
Unicode (UTF-16LE) O payload veio de ferramentas do Windows, de um valor de registro, ou de uma string .NET que foi codificada antes de viajar Cada caractere ganha um vão em volta, porque você leu um byte onde dois eram a intenção
ASCII Credenciais HTTP Basic clássicas e outros protocolos garantidamente de 7 bits Qualquer coisa acima do valor 127 vira um ponto de interrogação
Latin1 Texto europeu legacy anterior ao UTF-8 Sequências UTF-8 de múltiplos bytes se partem em várias letras erradas
Default Quase nunca. É a code page do sistema da máquina Seu script se comporta diferente em cada configuração regional do Windows

A falha clássica é texto UTF-8 decodificado como UTF-16. Os bytes são reais, o método fica satisfeito, e o resultado continua sendo lixo:

# "SGk=" são os bytes UTF-8 de "Hi"
[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String("SGk="))
# Um único caractere ilegível: 2 bytes de UTF-8 lidos como uma unidade UTF-16 de 2 bytes

A regra prática: se o texto decodificado parece que cada caractere tem um vão invisível em volta, ou parece um alfabeto diferente, você está uma codificação de distância. Pergunte onde os dados foram produzidos, e na dúvida, confie no UTF-8, mas verifique com os olhos os primeiros caracteres. E decida a codificação antes de decodificar, não depois que o mojibake aparecer no seu log.

base64url: o alfabeto que se dá bem em URLs

Você vai encontrar um primo do Base64 em cada token de API, JWT e identificador embutido em URL que você já tocou. O + e o / do Base64 padrão só são legais numa URL depois de percent-encoding, e o padding de iguais parece um separador de campos. Então o RFC 4648 definiu um alfabeto seguro para URL e nome de arquivo: os mesmos 64 caracteres, com a exceção de que o + vira hífen e o / vira sublinhado. O padding geralmente é descartado por completo, porque o comprimento dos dados o torna desnecessário. O RFC tem o cuidado de dizer que esta variante deve ser chamada de base64url e não só "base64", e o resto desta seção segue isso.

O .NET realmente fornece uma classe dedicada para ele, System.Buffers.Text.Base64Url, adicionada no .NET 9 com métodos rápidos de codificação e decodificação construídos inteiramente em torno de parâmetros ReadOnlySpan<T>. O PowerShell atual (7.4 e posteriores, uma vez que está rodando numa versão do .NET que fornece a classe) pode, na verdade, chamar esses overloads que aceitam span diretamente hoje em dia, graças a uma conversão implícita de array/string para span que o method binder agora faz, então [System.Buffers.Text.Base64Url]::DecodeFromChars("--__AQI") funciona sem cerimônia. Isso nem sempre foi verdade: o Windows PowerShell 5.1 e as versões antigas do PowerShell 7.x não conseguiam fazer bind de parâmetros span de jeito nenhum, e a classe nem existia antes do .NET 9 em primeiro lugar, então qualquer script que precisa rodar em 5.1, num 7.x antigo, ou num host pré-.NET-9 ainda precisa da versão portátil: troque os dois caracteres, e restaure o padding antes de entregar o texto ao decodificador padrão. O padding a adicionar é o que fizer o comprimento ser um múltiplo de quatro:

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

Duas armadilhas moram naquele bloquinho. Primeiro, a matemática do padding: um payload cujo comprimento já é múltiplo de quatro não precisa de padding, e a guarda -eq 4 é o que mantém a expressão honesta. Segundo, a direção: quando você só decodifica, você adiciona padding e troca os caracteres; você nunca remove padding de entrada de Base64 padrão, porque os decodificadores padrão esperam que ele esteja lá. Se a fonte é um JWT ou um token de API, ele será base64url sem padding, e a receita acima é exatamente a forma que você quer.

Abrindo um JWT sem as chaves

Um JSON Web Token é três segmentos base64url unidos por pontos: header, payload, assinatura. Os dois primeiros são JSON puro, e Base64 não é criptografia, então qualquer um com o token pode ler os dois. Isso é um recurso, não um defeito: o token foi feito para ser inspecionado, e é a assinatura que o torna infalsificável. O PowerShell transforma o espiolho em três linhas:

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

Três coisas para ter em mente. O terceiro segmento, a assinatura, também é base64url, mas ele decodifica para bytes binários de assinatura, não para texto, então não espere JSON bonito ali. O header geralmente só te diz qual algoritmo assinou o token (HS256, RS256, ...), e um header que diz none é um sinal de alerta, não uma conveniência. E ler o payload não é confiar nele: o base64 te deixa ver as claims, só a assinatura as torna autênticas. Se o seu trabalho é aceitar tokens, verifique a assinatura com a chave do emissor; se o seu trabalho é depurar um, o código acima é tudo que você precisa.

Arquivos, PEM e o caminho longo até os bytes

A forma de arquivo mais comum é um arquivo de texto guardando o Base64 de algo maior: um blob de backup, um binário baixado, um objeto serializado. A ida e volta são quatro linhas, e o modo moderno de tratar a saída é um array de bytes de verdade, não um palpite de texto:

$encoded = Get-Content -Path ./payload.b64 -Raw
$encoded = $encoded.Trim()
$bytes = [System.Convert]::FromBase64String($encoded)
[System.IO.File]::WriteAllBytes("./payload.bin", $bytes)
$bytes.Length
# quantos bytes o texto estava carregando

É ao ler o binário original de volta que o PowerShell 6 e os mais novos provam seu valor. O parâmetro -AsByteStream lê bytes crus, e junto com -Raw ele te entrega um byte[] genuíno de uma só vez:

$bytes = Get-Content -Path ./photo.png -AsByteStream -Raw
$encoded = [System.Convert]::ToBase64String($bytes)
Set-Content -Path ./photo.b64 -Value $encoded -NoNewline
$bytes.Length
# tamanho original, antes do imposto de texto de 33 por cento

Deixe o -Raw de fora e você fica com um stream de objetos byte individuais (um Object[] quando capturado), o que serve para inspeção, mas está errado para passar a métodos .NET que esperam um array. E o Windows PowerShell 5.1 não tem -AsByteStream de jeito nenhum, então no 5.1 a leitura confiável é [System.IO.File]::ReadAllBytes(), que existe em todos os lugares.

O PEM é o primo blindado que você conhece de todo certificado e chave privada: um corpo Base64 padrão, geralmente embrulhado a cada 64 caracteres, entre as linhas -----BEGIN ... e -----END .... A armadura é texto; o corpo é o payload. Remova a armadura, junte as linhas, decodifique:

$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
# o tamanho DER binário do certificado

Como o decodificador padrão ignora espaço em branco de qualquer jeito, o -join "" é cinto e suspensório em vez de exigência, mas deixar o script explícito sobre o que ele remove faz ele se comportar do mesmo jeito em toda máquina e toda convenção de final de linha. Na outra direção, embrulhar bytes DER em PEM é só o codificador Base64 mais duas linhas de texto, e o artigo de codificação no site irmão mostra o embrulho de 64 colunas em detalhes.

Certificados e a caixa de ferramentas do Windows

Certificados são os cidadãos Base64 mais pesados do trabalho diário, e o PowerShell aguenta a família inteira. Um arquivo PFX é um pacote binário de certificado mais chave privada, e é o formato que você mais encontra por aí vagando como texto Base64 em arquivos de configuração e scripts de deploy. Decodificar de volta para um certificado vivo é um one-liner com o tipo .NET, e funciona em plataforma cruzada no 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
# quando ele deixa de ser verdadeiro

O PowerShell 7 também fornece Get-PfxCertificate, que lê um arquivo PFX direto do disco com um parâmetro -Password, então para arquivos no disco você pode pular a decodificação manual por completo. Um certificado cru (sem chave) é ainda mais simples: os bytes DER vão direto para o mesmo tipo X509Certificate2 sem nenhuma senha.

Fora da linguagem, duas ferramentas nativas valem a pena conhecer. No Windows, certutil -decode infile.b64 outfile decodifica um arquivo Base64 com semântica de arquivo de entrada/arquivo de saída (adicione -f para sobrescrever), o que o torna o queridinho para correções rápidas num prompt de comando simples. Seu irmão certutil -encode tem uma flag que vale a pena lembrar: -unicodetext converte o texto de entrada para UTF-16 antes de codificá-lo em Base64, escondendo uma decisão inteira de codificação dentro de um único switch. No Linux e no macOS a utilidade clássica é base64 -d, que decodifica um arquivo ou o standard input, pulando quebras de linha por padrão; no GNU coreutils adicione -i se o payload também carregar espaços, tabulações ou CRLF de e-mail do Windows.

Comandos num envelope Base64

O PowerShell tem um motivo embutido para falar Base64 desde a versão 1.0: o parâmetro -EncodedCommand do próprio host. Você entrega ao pwsh uma string Base64, ele decodifica os bytes como UTF-16LE, e o resultado é executado como um comando. O propósito oficial, direto da documentação, é submeter comandos que precisam de aspas complexas ou chaves sem brigar com as regras de aspas do shell externo:

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

Leia aquela segunda linha com atenção, porque é onde todo mundo tropeça: o payload deve ser UTF-16LE, que é [System.Text.Encoding]::Unicode. Se você codificar o comando em UTF-8 no lugar, o PowerShell decodifica ele como UTF-16LE sem reclamar e executa um comando feito de mojibake, e a mensagem de erro que ele produz é um retrato perfeito do erro:

$wrong = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($command))
pwsh -NoProfile -EncodedCommand $wrong
# Erro: uma parede de caracteres ilegíveis, "The term ... is not recognized..."

É o mesmo mecanismo que faz equipes de segurança ligarem para Base64 no PowerShell. Um token longo e opaco passado para -EncodedCommand é uma forma comum em tooling automatizado, e é exatamente por isso que produtos de proteção de endpoint decodificam esses payloads antes de eles rodarem: nada no Base64 esconde o comando de um decodificador, ele só o esconde de um humano lendo uma lista de processos. Se você gera comandos codificados para a sua própria automação, mantenha o comando de origem ao lado do token, porque o token em si não vai se explicar sozinho às 3 da manhã.

Decodificando quando a entrada é enorme

Para tamanhos do dia a dia, a abordagem de método único é a rápida. Um binário de cinco megabytes vira uma string de uns seis milhões e novecentos mil caracteres, e a decodificação dessa string leva milissegundos de um só dígito numa máquina moderna. A própria nota da documentação do .NET é que FromBase64String foi desenhado para processar uma string única contendo todos os dados, o que é verdade, e o que também é tranquilo até limites muito grandes, porque o método trabalha na string no lugar, sem cópias extras significativas.

Quando o payload é maior do que você ficaria confortável guardando em uma string só, ou chega como um stream (um download, um socket, um log enorme), a ferramenta documentada é System.Security.Cryptography.FromBase64Transform embrulhada num CryptoStream: você alimenta com texto Base64 e lê os bytes decodificados, e apenas um pequeno buffer está vivo em qualquer momento. Note que TransformStream, o helper de C# para isso, é um método de extensão, e o PowerShell não vê métodos de extensão, então você instancia o CryptoStream diretamente:

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

Para noventa por cento dos trabalhos, o caminho simples continua sendo o certo: leia o arquivo de texto inteiro com Get-Content -Raw, remova as bordas, decodifique, escreva os bytes. Recorra à versão em stream quando o arquivo for grande demais para manter na memória com conforto, ou quando os dados estiverem chegando aos pedaços. E não tente fazer loop pelas linhas e decodificar cada linha separadamente: grupos de quatro caracteres do Base64 não respeitam suas quebras de linha, então uma linha que parte um grupo no meio não decodifica sozinha. Leia o texto inteiro, depois decodifique uma vez.

Armadilhas que custam tardes inteiras

  • O palpite da codificação. UTF-8 lido como UTF-16, ou Latin-1 lido como UTF-8, produz um mojibake cheio de confiança. Decida a codificação a partir da origem dos dados, use UTF-8 por padrão, e olhe os primeiros caracteres decodificados antes de confiar no resto.
  • Caracteres invisíveis vindos da web. Um espaço não separável ou um byte-order mark colado de uma página ou de um e-mail com formatação rica é um caractere estrangeiro para o decodificador e lança o FormatException genérico. Passe a entrada por .Trim() e uma verificação de caracteres não imprimíveis antes de decodificar.
  • Confusão de padding. O Base64 padrão chega com = ou == no final; o base64url de tokens chega sem nenhum. Alimentar um com a receita feita para o outro é a quebra silenciosa mais comum em trabalho com APIs, e a checagem de comprimento na seção de base64url é a guarda.
  • A mensagem única, três crimes. Como a mensagem de FormatException cobre caracteres inválidos, excesso de padding e padding sujo tudo de uma vez, blocos catch que só logam a mensagem te mandam em círculos. Logue também o comprimento da entrada e a primeira região ofensiva.
  • Esperar uma string de volta. O resultado é sempre um array de bytes. No momento em que você começa a formatá-lo como string diretamente, fica com uma lista de números, não texto. Converta com uma codificação explícita, uma vez, no final.
  • O padrão de arquivo do 5.1. O Windows PowerShell 5.1 lê arquivos sem BOM com a code page ANSI do sistema, enquanto o PowerShell 7 assume UTF-8. Se o seu script lê o arquivo de texto Base64 no 5.1 e o arquivo é UTF-8 com não-ASCII em volta do payload, a corrupção acontece antes do decodificador ver qualquer coisa.
  • Tratar Base64 como cadeado. É uma tradução. Uma senha, um token ou um segredo em Base64 é texto puro vestindo uma fantasia, e todo decodificador do planeta, incluindo este artigo, abre em uma linha.

Hábitos que mantêm os scripts honestos

  • Remova as bordas da entrada externa antes de decodificar. Um .Trim() só elimina mais incidentes de produção do que qualquer handler de erro.
  • Valide antes de decodificar quando a fonte não for confiável: depois de remover os quatro caracteres de espaço em branco permitidos, a string deve casar apenas com caracteres do alfabeto, com no máximo dois sinais de iguais no final. Uma checagem rápida de regex transforma uma exceção misteriosa numa mensagem limpa de entrada rejeitada.
  • Mantenha os bytes como bytes até o último passo. Decodifique uma vez, entregue o byte[] para a API de arquivo ou para o codificador que precisa dele, e só então converta para texto com uma codificação deliberada.
  • Logue comprimentos, não payloads. O tamanho da entrada e o tamanho da saída decodificada te contam quase tudo sobre uma falha de decodificação, sem colar dados potencialmente sensíveis no log.
  • Para qualquer coisa que cruze um fio, registre em qual alfabeto ela está, padrão ou base64url, e qual convenção de padding, na mesma linha de código que a decodifica. Você do futuro é o consumidor dessa nota.

Como o PowerShell herdou o seu decodificador

A história mais curta e verdadeira do Base64 no PowerShell é que o PowerShell nunca escreveu um. O método que você usa, Convert.FromBase64String, foi lançado com o .NET Framework 1.1 em 2003, e todo PowerShell desde a versão 1.0, em novembro de 2006, simplesmente expôs o .NET em que roda. O projeto se chamava Monad enquanto era construído, foi mostrado publicamente pela primeira vez na Professional Developers Conference em outubro de 2003, e na hora do lançamento o par codificador-decodificador do .NET que ele embrulha já tinha três anos e estava em uso diário.

O próprio formato foi padronizado no mesmo ano em que o shell saiu. O RFC 4648, publicado em outubro de 2006, é o documento que fixou o alfabeto, as regras de padding, a expectativa de decodificação estrita e a variante base64url, e ele ainda descreve exatamente o comportamento que FromBase64String implementa hoje. Quando o PowerShell ficou open-source e multiplataforma em agosto de 2016 como PowerShell Core, o decodificador veio junto para o Linux e o macOS sem mudanças, porque não havia nada para mudar.

O único acréscimo de verdade é o módulo mantido pela comunidade Microsoft.PowerShell.TextUtility do PowerShell Gallery, cujo cmdlet ConvertFrom-Base64 embrulha o mesmo método .NET e adiciona um switch -AsByteArray mais um padrão de texto que decodifica como UTF-8. Instale com Install-Module -Name Microsoft.PowerShell.TextUtility se você prefere a forma cmdlet, mas um alerta: o módulo agora está arquivado e não é mais mantido ativamente, o que é mais um motivo para o método embutido continuar sendo a recomendação para scripts novos.

Fatos que valem a pena lembrar

  • O decodificador ignora tabulações, line feeds, carriage returns e espaços em qualquer lugar da entrada. Cem linhas embrulhadas decodificam exatamente como uma linha longa.
  • $null e a string vazia decodificam para um array vazio sem reclamação, o que torna FromBase64String incomumente tolerante nos casos de borda.
  • A mensagem única de FormatException cobre três modos de falha diferentes. Quando ela dispara, a resposta está na entrada, não na mensagem.
  • "SABpAA==" é a string Hi na codificação interna do próprio PowerShell, UTF-16LE. É o dobro do tamanho da codificação UTF-8 das mesmas duas letras, e essa proporção é a impressão digital de texto nativo do Windows em qualquer Base64 que você for ler.
  • -EncodedCommand existe desde o primeiro lançamento do PowerShell, e o seu payload é obrigatoriamente UTF-16LE, não UTF-8. Codifique com a codificação errada e o shell executa seu mojibake de bom grado.
  • Os helpers Base64 baseados em span mais novos do .NET, incluindo a classe Base64Url, eram inalcançáveis a partir das versões antigas do PowerShell, porque spans são tipos do tipo byref que o method binder não conseguia ligar. Isso mudou: o PowerShell atual (7.4+, numa versão do .NET nova o suficiente para fornecer a classe) resolve um argumento de array ou string contra um parâmetro ReadOnlySpan<T> sem reclamação, então a chamada direta funciona hoje. A troca dos dois caracteres se mantém como a versão que também roda no Windows PowerShell 5.1 e em hosts mais antigos, e não como o único caminho que sobrou.
  • Get-Content -AsByteStream sem -Raw te dá um stream de objetos byte, não um array de bytes. Adicione -Raw e o tipo é exatamente o que os métodos .NET esperam.

O caminho longo de volta

Tudo neste artigo é sobre pegar uma string Base64 e trazer seus dados de volta. A operação espelho, transformar dados em Base64, parece um one-liner até você encontrar o fato de que strings do PowerShell não são bytes, de que o UTF-16 dobra seu tamanho, de que o embrulho de linhas tem duas larguras convencionais, e de que a saída base64url precisa da sua própria cirurgia de dois caracteres. Essa direção ganha o seu próprio tratamento completo, com as suas próprias armadilhas e a sua própria história, no artigo relacionado no site irmão, Codificação Base64 em PowerShell, vinculado abaixo nesta página.

Última atualização: 2026-09-07

Artigo relacionado: Codificação Base64 em PowerShell: um guia completo