Subir un archivo
  1. base64hub
  2. Base64 en código
  3. C#
Código

Base64 en C#

System.Convert trabaja con Base64 desde .NET Framework 1.0, y .NET 9 añadió System.Buffers.Text.Base64Url para cadenas URL-safe. Las salidas de esta página son de .NET 9.

Codificar y decodificar una cadena

Convert trabaja con bytes, así que elige una codificación de forma explícita. Para cualquier cosa que salga de tu proceso, casi siempre será UTF-8:

using System.Text;

string enc = Convert.ToBase64String(Encoding.UTF8.GetBytes("héllo wörld"));
Console.WriteLine(enc);   // aMOpbGxvIHfDtnJsZA==

string dec = Encoding.UTF8.GetString(Convert.FromBase64String(enc));
Console.WriteLine(dec);   // héllo wörld

Encoding.Unicode es UTF-16LE y duplica el tamaño del texto ASCII. Solo es la opción correcta cuando el otro extremo espera UTF-16, por ejemplo con el formato de comando codificado de PowerShell.

Un uso real habitual es la cabecera HTTP Basic Auth:

string token = Convert.ToBase64String(Encoding.UTF8.GetBytes("user:pass"));
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", token);
// Authorization: Basic dXNlcjpwYXNz

El generador de cabeceras Basic Auth produce el mismo valor en el navegador.

Base64 URL-safe: Base64Url en .NET 9

.NET 9 añadió System.Buffers.Text.Base64Url. Codifica sin relleno y decodifica con o sin él:

using System.Buffers.Text;

byte[] d = Encoding.UTF8.GetBytes("<<???>>");
Console.WriteLine(Base64Url.EncodeToString(d));            // PDw_Pz8-Pg

byte[] a = Base64Url.DecodeFromChars("PDw_Pz8-Pg");        // <<???>>
byte[] b = Base64Url.DecodeFromChars("PDw_Pz8-Pg==");      // <<???>>

Rechaza el alfabeto estándar: Base64Url.DecodeFromChars("PDw/Pz8+Pg") lanza FormatException. También hay sobrecargas basadas en spans (EncodeToChars, DecodeFromUtf8, TryDecodeFromChars) que evitan asignaciones de memoria.

En runtimes antiguos sin el paquete Microsoft.Bcl.Memory, convierte a mano:

string url = Convert.ToBase64String(d)
    .TrimEnd('=').Replace('+', '-').Replace('/', '_');    // PDw_Pz8-Pg

Para decodificar hacen falta los reemplazos inversos y rellenar hasta un múltiplo de 4:

static byte[] FromBase64Url(string s)
{
    s = s.Replace('-', '+').Replace('_', '/');
    s = s.PadRight(s.Length + (4 - s.Length % 4) % 4, '=');
    return Convert.FromBase64String(s);
}

Encoding.UTF8.GetString(FromBase64Url("PDw_Pz8-Pg"));   // <<???>>

Los proyectos ASP.NET Core pueden usar en su lugar WebEncoders.Base64UrlEncode de Microsoft.AspNetCore.WebUtilities. Más sobre los dos alfabetos en Base64 frente a Base64URL.

Validar la entrada sin excepciones

Convert.FromBase64String lanza System.FormatException con una entrada incorrecta. En .NET 9 el mensaje es el mismo en casi todos los casos:

The input is not a valid Base-64 string as it contains a non-base 64 character,
more than two padding characters, or an illegal character among the padding characters.

Obtuvimos ese mensaje con PDw_Pz8-Pg== (URL-safe), aGk=!! (basura), aGk (sin relleno), a y aGk==. Acepta espacios en blanco (" aGk= " y "aGVs\nbG8K" se decodifican bien) y bits de relleno distintos de cero (aGl=).

Para evitar excepciones en las rutas críticas, usa la variante Try con un búfer, o Base64.IsValid (.NET 8+):

Span<byte> buf = stackalloc byte[16];
Convert.TryFromBase64String("aGk=", buf, out int n);   // True, n = 2
Convert.TryFromBase64String("aGk", buf, out n);        // False, n = 0

System.Buffers.Text.Base64.IsValid("aGk=");      // True
System.Buffers.Text.Base64.IsValid("aGk");       // False
Base64Url.IsValid("PDw_Pz8-Pg");                 // True

El búfer debe ser lo bastante grande para la salida decodificada; si no, TryFromBase64String devuelve false incluso con una entrada válida. Dimensiónalo como (s.Length * 3) / 4. El validador Base64 y la guía de errores ayudan con las cadenas que fallan.

Saltos de línea (MIME)

Base64FormattingOptions.InsertLineBreaks corta la salida a 76 caracteres con \r\n, sin salto de línea final:

string mime = Convert.ToBase64String(
    Encoding.ASCII.GetBytes(new string('x', 60)),
    Base64FormattingOptions.InsertLineBreaks);
// "eHh4eHh4...eHh4" (76 chars) + "\r\n" + "eHh4"

FromBase64String vuelve a aceptar la forma con cortes sin cambios. El codificador Base64 MIME genera la misma disposición.

Codificar y decodificar un archivo

Console.WriteLine(Convert.ToBase64String(File.ReadAllBytes("hello.txt")));  // aGVsbG8K
File.WriteAllBytes("copy.txt", Convert.FromBase64String("aGVsbG8K"));

Archivos grandes con CryptoStream

ToBase64Transform y FromBase64Transform se conectan a un CryptoStream, que mantiene estable el uso de memoria. A pesar del espacio de nombres, no interviene ninguna criptografía:

using System.Security.Cryptography;

using (var input = File.OpenRead("big.bin"))
using (var output = File.Create("big.b64"))
using (var b64 = new CryptoStream(output, new ToBase64Transform(), CryptoStreamMode.Write))
{
    input.CopyTo(b64);
}

using (var input = new CryptoStream(File.OpenRead("big.b64"),
                                    new FromBase64Transform(), CryptoStreamMode.Read))
using (var output = File.Create("big.out"))
{
    input.CopyTo(output);
}

Al liberar el CryptoStream se vuelcan el último bloque y el relleno; si te lo saltas, la salida queda truncada. Con un archivo de prueba de 300 KB, las dos direcciones coincidieron con el resultado en memoria. Para una comprobación manual rápida, abre el resultado con Base64 a archivo.

Errores habituales

  • Prefijos de data URI. data:image/png;base64, contiene : y ;, que provocan FormatException. Quita todo lo que haya hasta la coma.
  • Tokens URL-safe en Convert. Las partes de un JWT fallan con el mensaje genérico de arriba. Usa Base64Url o el decodificador JWT.
  • Encoding.Default. En .NET Framework es la página de códigos ANSI; en .NET Core y posteriores es UTF-8. El código que pasa de uno a otro cambia la salida con texto no ASCII.
  • Base64 no es cifrado. Las cadenas de conexión «protegidas» con Base64 son texto plano para cualquiera. Consulta codificación frente a cifrado.

Notas de versión

  • .NET Framework 1.0+: Convert.ToBase64String, FromBase64String, ToBase64Transform.
  • .NET Core 2.1+: las sobrecargas con spans Convert.TryFromBase64String, TryToBase64Chars y la clase UTF-8 System.Buffers.Text.Base64.
  • .NET 8: Base64.IsValid.
  • .NET 9: Base64Url, también disponible para versiones anteriores mediante Microsoft.Bcl.Memory.

Preguntas frecuentes

¿Cómo soluciono «The input is not a valid Base-64 string»?

Busca caracteres URL-safe (-, _), relleno que falta y prefijos de data URI. Convert.FromBase64String solo acepta el alfabeto estándar con el relleno correcto. Para tokens, usa Base64Url.DecodeFromChars en .NET 9, que acepta entradas con y sin relleno.

¿Puedo usar Base64Url en .NET 8 o .NET Framework?

Base64Url viene integrado en .NET 9 y posteriores. Para versiones anteriores, Microsoft distribuye el mismo tipo en el paquete Microsoft.Bcl.Memory. Sin él, convierte la salida estándar con TrimEnd('=') y dos llamadas a Replace.

¿Por qué Encoding.Unicode no sirve para el Base64 de un texto?

Encoding.Unicode es UTF-16LE, así que hi se convierte en aABpAA== en lugar de aGk=. Usa Encoding.UTF8 salvo que el receptor espere UTF-16, como hace el -EncodedCommand de PowerShell.

Sigue leyendo