Todo projeto .NET com o qual trabalhei eventualmente precisa decodificar Base64 — buscando strings de conexão de secrets do Kubernetes, lendo payloads binários de webhooks ou inspecionando tokens JWT durante uma sessão de depuração. Para decodificar Base64 em C#, o método principal é Convert.FromBase64String(), que retorna um byte[] que você passa para Encoding.UTF8.GetString() para obter texto legível. Para uma verificação rápida sem escrever código, o decodificador Base64 do ToolDeck faz isso instantaneamente no navegador. Este guia cobre Convert.FromBase64String(), o TryFromBase64String() baseado em span para .NET 5+, a API de alto desempenho System.Buffers.Text.Base64, extração de payload JWT, decodificação de arquivos e respostas de API, streaming com CryptoStream e os quatro erros que mais afetam desenvolvedores C#.
- ✓Convert.FromBase64String(s) + Encoding.UTF8.GetString(bytes) é o pipeline padrão de dois passos — funciona em todas as versões do .NET.
- ✓Convert.TryFromBase64String() evita exceções em entrada inválida e escreve em um Span<byte> — ideal para caminhos críticos no .NET 5+.
- ✓System.Buffers.Text.Base64.DecodeFromUtf8() oferece decodificação zero-alocação para buffers de bytes UTF-8 em serviços com requisitos de desempenho.
- ✓Tokens JWT usam Base64url (- e _ em vez de + e /) — você precisa normalizar a entrada antes de chamar Convert.FromBase64String().
- ✓CryptoStream com FromBase64Transform trata decodificação em streaming para arquivos grandes sem carregar tudo na memória.
O que é decodificação Base64?
A codificação Base64 converte dados binários em um alfabeto ASCII de 64 caracteres para que sobreviva a transporte somente de texto — campos JSON, cabeçalhos HTTP, corpos de email, atributos XML. Cada 3 bytes de entrada se transformam em 4 caracteres Base64, por isso a saída Base64 é sempre cerca de 33% maior que o original. A decodificação reverte essa transformação. O padding = no final indica ao decodificador quantos bytes descartar do último grupo. Um único = significa que o último bloco tinha 2 bytes; == significa que tinha 1 byte. Base64 não é criptografia — qualquer pessoa pode revertê-lo. O objetivo é transporte seguro por canais que corrompem dados binários brutos, não confidencialidade.
cmVkaXM6Ly9jYWNoZS1wcm9kLmludGVybmFsOjYzNzkvc2Vzc2lvbi1zdG9yZQ==
redis://cache-prod.internal:6379/session-store
Convert.FromBase64String() — O método padrão de decodificação
O método Convert.FromBase64String() existe no .NET desde os dias do Framework 1.1. Sem pacotes NuGet, sem imports além de System — basta chamá-lo e receber um byte[]. O pipeline de dois passos para decodificar Base64 para uma string C# é sempre o mesmo: Convert.FromBase64String() para obter bytes, depois Encoding.UTF8.GetString() para interpretar esses bytes como texto. O detalhe é que o método retorna bytes brutos, não uma string. Você precisa escolher o Encoding correto para converter esses bytes de volta para texto, e essa escolha importa mais do que a maioria das pessoas espera. Um encoding incorreto produz silenciosamente mojibake — caracteres embaralhados sem nenhuma exceção para alertar você.
Exemplo mínimo funcional
using System; using System.Text; // String de conexão armazenada como Base64 em um secret do Kubernetes string encoded = "cmVkaXM6Ly9jYWNoZS1wcm9kLmludGVybmFsOjYzNzkvc2Vzc2lvbi1zdG9yZQ=="; byte[] decodedBytes = Convert.FromBase64String(encoded); string connectionString = Encoding.UTF8.GetString(decodedBytes); Console.WriteLine(connectionString); // redis://cache-prod.internal:6379/session-store
Sempre use Encoding.UTF8 a não ser que tenha um motivo específico para não usar. O runtime do .NET representa strings internamente como UTF-16, mas a maioria dos dados que cruzam fronteiras de sistema (respostas de API, arquivos de configuração, secrets) é codificada como UTF-8. Usar Encoding.ASCII em dados que contêm caracteres multibyte os substitui silenciosamente por ? — sem exceção, apenas saída corrompida.
Verificação de ida e volta
using System; using System.Text; string original = "postgres://db-admin:Kx8!mQ@db-prod.us-east-1.internal:5432/orders"; // Codificar string encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(original)); Console.WriteLine(encoded); // cG9zdGdyZXM6Ly9kYi1hZG1pbjpLeDghbVFAZGItcHJvZC51cy1lYXN0LTEuaW50ZXJuYWw6NTQzMi9vcmRlcnM= // Decodificar byte[] decoded = Convert.FromBase64String(encoded); string recovered = Encoding.UTF8.GetString(decoded); Console.WriteLine(recovered == original); // True
Escolhendo o Encoding correto
O Encoding que você passa para GetString() deve corresponder ao que foi usado quando os dados foram originalmente codificados. Escolha o errado e você obtém caracteres inválidos sem nenhuma exceção — o decodificador felizmente produz lixo. Aqui está o resumo prático:
Encoding.UTF8— padrão seguro. Trata ASCII e todo Unicode. Use este a não ser que saiba o contrário.Encoding.ASCII— apenas para dados ASCII puro de 7 bits. Caracteres multibyte viram?.Encoding.Unicode— este é UTF-16LE no .NET. Algumas strings internas do Windows e exportações do PowerShell usam isso.Encoding.Latin1— encoding legado da Europa Ocidental. Aparece em serviços SOAP antigos e integrações com mainframe.
using System;
using System.Text;
// Mesmos bytes, encodings diferentes — resultados diferentes
byte[] decoded = Convert.FromBase64String("w7bDvMOk");
Console.WriteLine(Encoding.UTF8.GetString(decoded)); // oua (correto)
Console.WriteLine(Encoding.ASCII.GetString(decoded)); // ?????? (perdido)
Console.WriteLine(Encoding.Latin1.GetString(decoded)); // öüä (mojibake)Um helper de decodificação reutilizável com tratamento de erros
Como Convert.FromBase64String() lança em entradas inválidas e a dança de remoção de espaços em branco acontece constantemente, mantenho um pequeno helper na maioria dos projetos:
using System;
using System.Text;
static class Base64Helper
{
public static string? DecodeToString(string encoded)
{
if (string.IsNullOrWhiteSpace(encoded))
return null;
// Remover espaços em branco que o decodificador do .NET rejeita
string cleaned = encoded
.Replace("
", "")
.Replace("
", "")
.Replace(" ", "")
.Trim();
try
{
byte[] bytes = Convert.FromBase64String(cleaned);
return Encoding.UTF8.GetString(bytes);
}
catch (FormatException)
{
return null; // ou lançar uma exceção específica do domínio
}
}
}
// Uso
string? decoded = Base64Helper.DecodeToString(" cmVkaXM6Ly9jYWNoZQ== \n");
Console.WriteLine(decoded); // redis://cacheConvert.FromBase64String() lança FormatException se a entrada contiver caracteres fora do alfabeto Base64 — incluindo espaços, quebras de linha e caracteres seguros para URL como - e _. O helper acima trata espaços em branco automaticamente.Decodificando Base64 em tipos não padrão
O decodificador sempre retorna byte[]. O que você faz com esses bytes depende dos dados originais. Às vezes é um GUID armazenado como 16 bytes brutos, às vezes é uma mensagem protobuf serializada, às vezes é um timestamp em formato binário. Aqui estão as conversões que uso com mais frequência.
Base64 para GUID
using System; // Algumas APIs enviam GUIDs como Base64 de 22 chars em vez de strings hex de 36 chars string compactGuid = "C0HqetxMckKlZw4CssPUeQ=="; byte[] guidBytes = Convert.FromBase64String(compactGuid); Guid recovered = new Guid(guidBytes); Console.WriteLine(recovered); // 7aea41c0-4cdc-4272-a567-0e02b2c3d479
Base64 para JSON deserializado com System.Text.Json
using System;
using System.Text;
using System.Text.Json;
// Payload JSON codificado em Base64 de uma fila de mensagens
string encoded = "eyJzZXJ2aWNlIjoicGF5bWVudC1nYXRld2F5IiwicmVnaW9uIjoiZXUtd2VzdC0xIiwicmVwbGljYXMiOjR9";
byte[] jsonBytes = Convert.FromBase64String(encoded);
string json = Encoding.UTF8.GetString(jsonBytes);
Console.WriteLine(json);
// {"service":"payment-gateway","region":"eu-west-1","replicas":4}
// Desserializar em um record
var deployEvent = JsonSerializer.Deserialize<DeployEvent>(jsonBytes);
Console.WriteLine($"{deployEvent!.Service} in {deployEvent.Region}");
// payment-gateway in eu-west-1
record DeployEvent(string Service, string Region, int Replicas);Base64 para string hexadecimal
using System; // Hash SHA-256 armazenado como Base64 string hashBase64 = "n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg="; byte[] hashBytes = Convert.FromBase64String(hashBase64); string hex = Convert.ToHexString(hashBytes); Console.WriteLine(hex); // 9F86D081884C7D659A2FEAA0C55AD015A3BF4F1B2B0B822CD15D6C15B0F00A08
BinaryFormatter. Ele tem vulnerabilidades conhecidas de execução remota de código e está obsoleto no .NET 8+. Se o conteúdo codificado vier de uma fonte externa, analise-o como JSON ou protobuf em vez de usar serialização binária do .NET.Referência dos métodos de decodificação Base64
O .NET fornece múltiplos métodos de decodificação em dois namespaces. A classe Convert trata decodificação de uso geral, enquanto System.Buffers.Text.Base64 é voltada para cenários de alto throughput onde você já trabalha com buffers de bytes UTF-8 brutos.
Convert.TryFromBase64String() — Decodificação sem exceções
O padrão Try é padrão no .NET para operações que podem falhar com entrada do usuário. Convert.TryFromBase64String(), disponível desde o .NET 5, retorna um bool em vez de lançar FormatException. Ele escreve os bytes decodificados em um Span<byte> fornecido pelo chamador, o que significa que você pode usar memória alocada na pilha para payloads pequenos e evitar completamente o heap.
using System;
using System.Text;
string userInput = "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=";
// Alocar na pilha para payloads pequenos (< 1KB é uma boa regra prática)
Span<byte> buffer = stackalloc byte[256];
if (Convert.TryFromBase64String(userInput, buffer, out int bytesWritten))
{
string result = Encoding.UTF8.GetString(buffer[..bytesWritten]);
Console.WriteLine(result);
// {"host":"10.0.1.50","port":8443}
}
else
{
Console.WriteLine("Entrada Base64 inválida — ignorando");
}Essa abordagem é excelente em middleware de requisições ou pipelines de validação onde entrada inválida é esperada, não excepcional. Lançar e capturar uma FormatException em cada requisição malformada adiciona overhead mensurável em escala — TryFromBase64String() evita isso completamente.
Validando entrada Base64 sem decodificar
Um padrão comum em controllers de API: verificar se a entrada é Base64 válida antes de passá-la adiante. Você pode usar TryFromBase64String() como validador alocando um buffer descartável:
using System;
static bool IsValidBase64(string input)
{
// Calcular o tamanho máximo decodificado
Span<byte> buffer = stackalloc byte[((input.Length + 3) / 4) * 3];
return Convert.TryFromBase64String(input, buffer, out _);
}
// Uso em um controller de API
Console.WriteLine(IsValidBase64("eyJob3N0IjoiMTAuMC4xLjUwIn0=")); // True
Console.WriteLine(IsValidBase64("not!!valid!!base64")); // False
Console.WriteLine(IsValidBase64("")); // True (vazio é válido)TryFromBase64String() retorna false mesmo quando a entrada é válida. Calcule o tamanho necessário como (inputLength / 4) * 3 para garantir.Decodificar Base64 de um arquivo e resposta de API
Lendo um arquivo codificado em Base64 do disco
Certificados, blobs criptografados e arquivos de exportação de dados às vezes são distribuídos como texto Base64. O padrão típico: leia o arquivo como string, remova quaisquer espaços em branco ou quebras de linha que causariam FormatException, decodifique para bytes e escreva a saída binária. Preste atenção no tratamento de erros — erros de I/O de arquivo e erros de formato Base64 devem ser capturados separadamente.
using System;
using System.IO;
string inputPath = "tls-cert.pem.b64";
string outputPath = "tls-cert.pem";
try
{
string encoded = File.ReadAllText(inputPath).Trim();
// Remover quebras de linha — o decodificador do .NET as rejeita
encoded = encoded.Replace("
", "").Replace("
", "");
byte[] decoded = Convert.FromBase64String(encoded);
File.WriteAllBytes(outputPath, decoded);
Console.WriteLine($"Decoded {decoded.Length} bytes -> {outputPath}");
}
catch (IOException ex)
{
Console.Error.WriteLine($"File error: {ex.Message}");
}
catch (FormatException ex)
{
Console.Error.WriteLine($"Invalid Base64: {ex.Message}");
}Decodificando um campo Base64 de uma resposta de API HTTP
APIs de nuvem (Azure Key Vault, AWS Secrets Manager, GitHub Contents API) frequentemente retornam dados binários como strings Base64 embutidas em JSON. O fluxo é sempre o mesmo: faça a requisição HTTP, analise a resposta JSON, extraia o campo Base64 e decodifique. O exemplo abaixo usa HttpClient e System.Text.Json — ambos embutidos no .NET 6+.
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer sk-prod-9f8e7d6c");
try
{
var response = await client.GetAsync("https://api.example.com/secrets/db-password");
response.EnsureSuccessStatusCode();
string body = await response.Content.ReadAsStringAsync();
// API retorna: {"name":"db-password","value":"cG9zdGdyZXM6eGs5bVAycVI=","version":3}
using var doc = JsonDocument.Parse(body);
string encodedValue = doc.RootElement.GetProperty("value").GetString()!;
byte[] decoded = Convert.FromBase64String(encodedValue);
string secret = Encoding.UTF8.GetString(decoded);
Console.WriteLine($"Secret: {secret}");
// Secret: postgres:xk9mP2qR
}
catch (HttpRequestException ex)
{
Console.Error.WriteLine($"HTTP error: {ex.Message}");
}
catch (FormatException ex)
{
Console.Error.WriteLine($"Base64 decode failed: {ex.Message}");
}catch para erros de rede e FormatException. Agrupá-los dificulta saber se a API retornou dados inválidos ou se a própria requisição falhou. Em produção, registre o valor Base64 bruto (ou pelo menos seu comprimento e os primeiros 20 caracteres) ao capturar FormatException — isso torna a depuração dramaticamente mais fácil.Decodificação Base64 pela linha de comando
Nem sempre você precisa de um projeto compilado. A ferramenta dotnet-script e o PowerShell tratam decodificação Base64 com one-liners. Para inspeção rápida durante a depuração, esses são mais rápidos do que criar um aplicativo de console.
# PowerShell (embutido no Windows, disponível no Linux/macOS)
[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("eyJob3N0IjoiMTAuMC4xLjUwIn0="))
# {"host":"10.0.1.50"}
# Comando base64 nativo do Linux / macOS
echo "eyJob3N0IjoiMTAuMC4xLjUwIn0=" | base64 --decode
# {"host":"10.0.1.50"}
# macOS usa -D em vez de --decode
echo "eyJob3N0IjoiMTAuMC4xLjUwIn0=" | base64 -D
# dotnet-script (instalar: dotnet tool install -g dotnet-script)
echo 'Console.WriteLine(System.Text.Encoding.UTF8.GetString(Convert.FromBase64String("eyJob3N0IjoiMTAuMC4xLjUwIn0=")));' | dotnet-script eval
# Decodificar e formatar JSON com jq
echo "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=" | base64 --decode | jq .Para colar strings codificadas diretamente no navegador, o decodificador Base64 do ToolDeck trata variantes padrão e seguras para URL sem nenhuma configuração.
Alternativa de alto desempenho: System.Buffers.Text.Base64
A classe System.Buffers.Text.Base64, disponível desde o .NET Core 2.1, opera em spans de bytes UTF-8 brutos em vez de strings .NET. Isso ignora completamente o overhead de conversão string-para-byte — sem alocação intermediária de string, sem etapa de encoding UTF-16. Uso isso em middleware ASP.NET Core onde os dados de entrada já são um ReadOnlySpan<byte> do corpo da requisição. Criar uma string apenas para passá-la a Convert.FromBase64String() dobra as alocações sem motivo. Em testes com BenchmarkDotNet no .NET 8, o caminho baseado em span é cerca de 2-3x mais rápido para payloads abaixo de 1 KB e a diferença aumenta com entradas maiores porque a pressão no GC permanece constante.
using System;
using System.Buffers.Text;
using System.Text;
// Simular bytes UTF-8 brutos do corpo de uma requisição HTTP
byte[] utf8Input = Encoding.UTF8.GetBytes("eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0M30=");
byte[] output = new byte[Base64.GetMaxDecodedFromUtf8Length(utf8Input.Length)];
OperationStatus status = Base64.DecodeFromUtf8(
utf8Input, output, out int bytesConsumed, out int bytesWritten);
if (status == OperationStatus.Done)
{
string result = Encoding.UTF8.GetString(output.AsSpan(0, bytesWritten));
Console.WriteLine(result);
// {"host":"10.0.1.50","port":8443}
Console.WriteLine($"Consumed: {bytesConsumed}, Written: {bytesWritten}");
// Consumed: 44, Written: 31
}
else
{
Console.WriteLine($"Decode failed: {status}");
// Possíveis: InvalidData, DestinationTooSmall, NeedMoreData
}O tipo de retorno é OperationStatus — um enum com quatro valores: Done, InvalidData, DestinationTooSmall, e NeedMoreData. O último é útil para decodificação parcial de dados em streaming. Para zero alocação absoluta, combine com ArrayPool<byte>.Shared.Rent() em vez de new byte[].
DecodeFromUtf8InPlace — sobrescrever o buffer de entrada
using System;
using System.Buffers.Text;
using System.Text;
// O buffer de entrada é sobrescrito com os bytes decodificados
byte[] data = Encoding.UTF8.GetBytes("c2VydmVyLWNvbmZpZw==");
OperationStatus status = Base64.DecodeFromUtf8InPlace(data, out int bytesWritten);
if (status == OperationStatus.Done)
{
Console.WriteLine(Encoding.UTF8.GetString(data.AsSpan(0, bytesWritten)));
// server-config
}DecodeFromUtf8InPlace modifica o buffer de entrada. Não use este método se precisar da string Base64 original depois — os primeiros bytesWritten bytes do array agora contêm os dados decodificados, e o restante é lixo.Um detalhe importante: System.Buffers.Text.Base64 não trata Base64 seguro para URL diretamente. Se a entrada usar - e _ (tokens JWT, por exemplo), você ainda precisa substituí-los por + e / antes de chamar esses métodos. As APIs baseadas em span seguem estritamente o alfabeto padrão RFC 4648. Não há variante DecodeFromUtf8Url — uma lacuna surpreendente dado o quão comum o Base64url é nas APIs modernas.
Saída no terminal com realce de sintaxe
A biblioteca Spectre.Console oferece saída rica no terminal incluindo realce de JSON — útil ao criar ferramentas de CLI que decodificam Base64 e exibem o resultado. Instale com dotnet add package Spectre.Console.
using System;
using System.Text;
using Spectre.Console;
using Spectre.Console.Json;
string encoded = "eyJob3N0IjoiMTAuMC4xLjUwIiwicG9ydCI6ODQ0MywibWF4Q29ubiI6MTAwfQ==";
byte[] decoded = Convert.FromBase64String(encoded);
string json = Encoding.UTF8.GetString(decoded);
// Formatar com realce de sintaxe no terminal
AnsiConsole.Write(new JsonText(json));
// Saída com JSON colorido:
// {
// "host": "10.0.1.50",
// "port": 8443,
// "maxConn": 100
// }Isso é especialmente útil para ferramentas de CLI que buscam e decodificam configurações de serviços remotos. Decodifique a configuração Base64, desserialize para JSON e imprima a saída colorida — tudo em poucas linhas. Spectre.Console também tem renderização de tabelas, barras de progresso e visualizações em árvore se você precisar exibir estruturas de dados decodificadas mais complexas no terminal.
Streaming de grandes arquivos Base64 com CryptoStream
Carregar um arquivo Base64 de 500 MB com File.ReadAllText() e depois chamar Convert.FromBase64String() aloca cerca de 700 MB no heap: a própria string (UTF-16, portanto o dobro do tamanho do arquivo) mais o array de bytes decodificado. A combinação CryptoStream + FromBase64Transform decodifica em partes, mantendo o uso de memória constante.
using System.IO;
using System.Security.Cryptography;
string inputPath = "database-export.sql.b64";
string outputPath = "database-export.sql";
using var inputStream = new FileStream(inputPath, FileMode.Open, FileAccess.Read);
using var transform = new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces);
using var base64Stream = new CryptoStream(inputStream, transform, CryptoStreamMode.Read);
using var outputStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write);
base64Stream.CopyTo(outputStream);
Console.WriteLine($"Decoded {new FileInfo(outputPath).Length} bytes -> {outputPath}");O flag FromBase64TransformMode.IgnoreWhiteSpaces trata Base64 com quebras de linha (arquivos PEM, exportações de email) sem limpeza manual. Sem este flag, quebras de linha na entrada causam FormatException. Este é o equivalente mais próximo do .NET ao getMimeDecoder() do Java — ignora silenciosamente caracteres de espaço em branco durante a decodificação.
O nome CryptoStream é enganoso — não há nada criptográfico no Base64. A Microsoft colocou FromBase64Transform no namespace System.Security.Cryptography porque ele implementa ICryptoTransform, a mesma interface usada para AES e outras transformações de cifra. O stream em si apenas canaliza dados por qualquer transformação em partes. Pense nele como um pipeline de transformação de streaming de uso geral que por acaso vive no namespace errado.
Streaming assíncrono para cenários ASP.NET Core
using System.IO;
using System.Security.Cryptography;
async Task DecodeStreamAsync(Stream inputStream, string outputPath)
{
using var transform = new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces);
using var base64Stream = new CryptoStream(inputStream, transform, CryptoStreamMode.Read);
await using var outputStream = new FileStream(
outputPath, FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 81920, useAsync: true);
await base64Stream.CopyToAsync(outputStream);
}
// Uso em um endpoint:
// await DecodeStreamAsync(Request.Body, "uploaded-file.bin");CopyToAsync em vez de CopyTo em handlers ASP.NET Core. I/O síncrono na thread de requisição é bloqueado por padrão no Kestrel e lança InvalidOperationException.Como decodificar o payload Base64 de um JWT em C#
Um JWT tem três segmentos codificados em Base64url separados por pontos. O segmento do meio é o payload. Você pode decodificá-lo sem recorrer a uma biblioteca JWT — divida no ., normalize os caracteres Base64url, corrija o padding e chame Convert.FromBase64String(). Isso derruba quase todo mundo na primeira vez porque JWT usa - e _ em vez de + e /, e remove o padding =.
using System;
using System.Text;
static string DecodeJwtPayload(string token)
{
string[] parts = token.Split('.');
if (parts.Length != 3)
throw new ArgumentException($"Invalid JWT: expected 3 segments, got {parts.Length}");
// Pegar o payload (segundo segmento)
string payload = parts[1];
// Substituir caracteres seguros para URL por Base64 padrão
payload = payload.Replace('-', '+').Replace('_', '/');
// Completar até múltiplo de 4
switch (payload.Length % 4)
{
case 2: payload += "=="; break;
case 3: payload += "="; break;
}
byte[] bytes = Convert.FromBase64String(payload);
return Encoding.UTF8.GetString(bytes);
}
// Testar com um token de formato real
string token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9"
+ ".eyJzdWIiOiJ1c3ItNjcyIiwiaXNzIjoiYXV0aC5leGFtcGxlLmNvbSIsImV4cCI6MTc0MTk1NjgwMCwicm9sZXMiOlsiYWRtaW4iLCJiaWxsaW5nIl19"
+ ".SIGNATURE_PLACEHOLDER";
Console.WriteLine(DecodeJwtPayload(token));
// {"sub":"usr-672","iss":"auth.example.com","exp":1741956800,"roles":["admin","billing"]}Observação rápida: isso apenas lê o payload. Não verifica a assinatura. Para validação de autenticação em produção, use uma biblioteca adequada como Microsoft.IdentityModel.JsonWebTokens. Mas para depuração, logging e asserções de teste, essa abordagem manual é tudo que você precisa.
Convertendo o payload JWT decodificado em um objeto tipado
Uma vez que você tem a string JSON, desserialize-a com System.Text.Json para acessar claims individuais sem manipulação de strings:
using System;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
record JwtPayload(
[property: JsonPropertyName("sub")] string Subject,
[property: JsonPropertyName("iss")] string Issuer,
[property: JsonPropertyName("exp")] long Expiration,
[property: JsonPropertyName("roles")] string[] Roles
);
// Após DecodeJwtPayload() acima
string json = DecodeJwtPayload(token);
var claims = JsonSerializer.Deserialize<JwtPayload>(json)!;
Console.WriteLine($"User: {claims.Subject}");
Console.WriteLine($"Issuer: {claims.Issuer}");
Console.WriteLine($"Expires: {DateTimeOffset.FromUnixTimeSeconds(claims.Expiration):u}");
Console.WriteLine($"Roles: {string.Join(", ", claims.Roles)}");
// User: usr-672
// Issuer: auth.example.com
// Expires: 2025-03-14 12:00:00Z
// Roles: admin, billingErros comuns
Já cometi todos esses erros em serviços C# em produção. Os dois primeiros são responsáveis pela maioria dos bugs relacionados a Base64 que vejo em revisões de código. Cada erro parece óbvio isoladamente, mas é fácil de ignorar quando você está fundo em uma funcionalidade maior e a decodificação Base64 é apenas um passo no pipeline.
Problema: Strings Base64 lidas de arquivos de configuração, variáveis de ambiente ou entrada do usuário frequentemente têm quebras de linha no final. Convert.FromBase64String() rejeita qualquer caractere fora do alfabeto Base64, incluindo \r\n.
Correção: Chame .Trim() ou .Replace() para remover espaços em branco antes de decodificar.
// Variável de ambiente tem uma quebra de linha no final
string encoded = Environment.GetEnvironmentVariable("DB_PASS_B64")!;
byte[] decoded = Convert.FromBase64String(encoded);
// FormatException: The input is not a valid Base-64 stringstring encoded = Environment.GetEnvironmentVariable("DB_PASS_B64")!;
string cleaned = encoded.Replace("\n", "").Replace("\r", "").Trim();
byte[] decoded = Convert.FromBase64String(cleaned);
Console.WriteLine(Encoding.UTF8.GetString(decoded));Problema: Tokens JWT e alguns payloads de API usam - e _ (alfabeto seguro para URL). O decodificador padrão aceita apenas + e / — ele lança FormatException no primeiro caractere -.
Correção: Substitua - por + e _ por / antes de decodificar. Corrija também o padding.
// Payload JWT — usa Base64 seguro para URL string payload = "eyJzdWIiOiJ1c3ItNjcyIn0"; byte[] decoded = Convert.FromBase64String(payload); // FormatException
string payload = "eyJzdWIiOiJ1c3ItNjcyIn0";
payload = payload.Replace('-', '+').Replace('_', '/');
switch (payload.Length % 4)
{
case 2: payload += "=="; break;
case 3: payload += "="; break;
}
byte[] decoded = Convert.FromBase64String(payload);
Console.WriteLine(Encoding.UTF8.GetString(decoded));
// {"sub":"usr-672"}Problema: Chamar Encoding.UTF8.GetString() em bytes decodificados de uma imagem ou protobuf produz lixo. Pior, converter essa string de volta para bytes corrompe silenciosamente os dados porque sequências UTF-8 inválidas são substituídas.
Correção: Mantenha dados binários como byte[] durante todo o pipeline. Chame GetString() apenas quando souber que o conteúdo é texto.
byte[] decoded = Convert.FromBase64String(pngBase64);
string imageStr = Encoding.UTF8.GetString(decoded); // corrompe o binário
File.WriteAllText("image.png", imageStr); // arquivo quebradobyte[] decoded = Convert.FromBase64String(pngBase64);
// Escrever bytes diretamente — sem conversão para string
File.WriteAllBytes("image.png", decoded);Problema: Algumas APIs mais antigas do .NET Framework usam a página de código ANSI atual do sistema, que varia entre máquinas. Um servidor Windows com a página de código 1252 e um container Linux com UTF-8 produzem strings diferentes dos mesmos bytes.
Correção: Sempre especifique Encoding.UTF8 explicitamente. Nunca dependa do padrão da plataforma.
byte[] decoded = Convert.FromBase64String(encoded); // Encoding.Default varia entre plataformas string result = Encoding.Default.GetString(decoded);
byte[] decoded = Convert.FromBase64String(encoded); string result = Encoding.UTF8.GetString(decoded); // consistente no Windows, Linux, macOS
Comparação de métodos
O .NET oferece mais métodos de decodificação Base64 do que a maioria dos desenvolvedores percebe. A tabela abaixo cobre todas as opções embutidas mais as duas alternativas de terceiros mais comuns. A coluna "Alocação" importa mais em serviços de alto throughput — um método que aloca um novo byte[] a cada chamada pressiona o GC em loops intensos.
Para uso cotidiano: Convert.FromBase64String(). Para caminhos críticos onde você valida entrada do usuário: TryFromBase64String(). Para middleware ASP.NET Core operando em bytes brutos de requisição: Base64.DecodeFromUtf8(). Para arquivos grandes: CryptoStream + FromBase64Transform. BouncyCastle só faz sentido se já estiver na sua árvore de dependências para outras operações criptográficas.
Para verificação rápida sem compilar nada, o decodificador Base64 online é mais rápido do que escrever um aplicativo de console descartável.
Perguntas frequentes
Como decodifico uma string Base64 para texto em C#?
Chame Convert.FromBase64String() para obter um array de bytes e depois passe-o para Encoding.UTF8.GetString(). O encoding deve corresponder ao que foi usado na codificação — UTF-8 é o padrão seguro para quase todos os sistemas modernos. Se a entrada puder conter espaços em branco ou quebras de linha, chame .Trim() ou remova-os antes de decodificar.
using System; using System.Text; string encoded = "cG9zdGdyZXM6eGs5bVAycVI="; byte[] bytes = Convert.FromBase64String(encoded); string result = Encoding.UTF8.GetString(bytes); Console.WriteLine(result); // postgres:xk9mP2qR
Qual é a diferença entre Convert.FromBase64String() e Convert.TryFromBase64String()?
FromBase64String() lança uma FormatException em entrada inválida. TryFromBase64String() retorna um bool e escreve o resultado em um Span<byte> fornecido pelo chamador, tornando-o adequado para caminhos críticos onde você quer evitar o overhead de exceções. TryFromBase64String() requer .NET 5 ou posterior.
using System;
string input = "maybe-not-valid-base64!!";
Span<byte> buffer = stackalloc byte[256];
if (Convert.TryFromBase64String(input, buffer, out int written))
Console.WriteLine($"Decoded {written} bytes");
else
Console.WriteLine("Invalid Base64 input");Como decodifico um payload Base64url de JWT em C#?
Divida o token nos pontos, pegue o segundo segmento, substitua - por + e _ por /, complete com = até múltiplo de 4 e chame Convert.FromBase64String(). Tokens JWT usam o alfabeto Base64 seguro para URL, que o decodificador padrão do .NET não trata diretamente.
string token = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c3ItNjcyIn0.SIG";
string payload = token.Split('.')[1];
payload = payload.Replace('-', '+').Replace('_', '/');
switch (payload.Length % 4)
{
case 2: payload += "=="; break;
case 3: payload += "="; break;
}
byte[] bytes = Convert.FromBase64String(payload);
Console.WriteLine(Encoding.UTF8.GetString(bytes));
// {"sub":"usr-672"}Como escolher o Encoding correto ao decodificar Base64 em C#?
Use Encoding.UTF8 como padrão — ele trata ASCII e caracteres Unicode multibyte. Use Encoding.ASCII apenas quando tiver certeza de que os dados são ASCII puro de 7 bits. Use Encoding.Unicode (que é UTF-16LE no .NET) somente quando os dados originais foram codificados como UTF-16, o que ocorre às vezes com strings internas do Windows e exportações do PowerShell.
Por que Convert.FromBase64String() lança FormatException?
Três causas comuns: a entrada contém espaços em branco ou quebras de linha (remova-os antes de decodificar), a entrada usa caracteres seguros para URL como - e _ (substitua-os por + e / respectivamente), ou o padding está ausente ou incorreto (o comprimento total deve ser múltiplo de 4 após o padding). Ao contrário do Java, o .NET não tem um decodificador MIME embutido que tolere espaços em branco — você precisa limpar a entrada ou usar CryptoStream com FromBase64Transform no modo IgnoreWhiteSpaces.
// Corrigir espaços em branco
string cleaned = rawInput.Replace("\n", "").Replace("\r", "").Trim();
byte[] decoded = Convert.FromBase64String(cleaned);Posso decodificar grandes dados Base64 em streaming em C#?
Sim. Use um CryptoStream com FromBase64Transform de System.Security.Cryptography. Isso decodifica em partes conforme você lê, mantendo o uso de memória constante independentemente do tamanho do arquivo. Passe FromBase64TransformMode.IgnoreWhiteSpaces se a entrada contiver quebras de linha. No .NET 6+, você também pode usar o padrão IAsyncEnumerable com Base64.DecodeFromUtf8() para processamento em chunks manual, embora CryptoStream seja mais simples para decodificação de arquivo a arquivo.
using System.IO;
using System.Security.Cryptography;
using var input = File.OpenRead("payload.b64");
using var transform = new FromBase64Transform();
using var cryptoStream = new CryptoStream(input, transform, CryptoStreamMode.Read);
using var output = File.Create("payload.bin");
cryptoStream.CopyTo(output);Ferramentas relacionadas
- Base64 Encoder — codifique texto ou dados binários para Base64 no navegador, útil para gerar fixtures de teste para colar nos seus testes unitários C#.
- JWT Decoder — decodifique e inspecione todos os três segmentos JWT de uma vez, com inspeção campo a campo do payload — mais rápido do que escrever um helper C# quando você só precisa ler um token.
- URL Decoder — decodifique strings codificadas por percent-encoding, útil quando respostas de API misturam dados Base64url com parâmetros de query percent-encoded.
- JSON Formatter — após decodificar um payload JWT Base64 ou configuração de API, cole o JSON aqui para formatar e validar a estrutura.