The .NET Convert Mental Model
Base64 in C# lives on the static System.Convertclass, not in a dedicated encoding namespace. This matches .NET's design: conversions between primitive types cluster under Convert, and byte[] ↔ Base64 string is treated as another primitive conversion.
The four methods you'll actually use:
Convert.ToBase64String(byte[])— encode bytes to string. The everyday call.Convert.FromBase64String(string)— decode string back to byte[]. ThrowsFormatExceptionon invalid input.Convert.TryFromBase64String(string, Span<byte>, out int)— no-throw decode; returns false on invalid input.Convert.TryToBase64Chars(ReadOnlySpan<byte>, Span<char>, out int)— zero-alloc encode into caller-owned span.
Encoding a String — The Standard Pattern
using System;
using System.Text;
string text = "Hello 世界 👋";
// Step 1: string → byte[] (UTF-8 is the correct choice for arbitrary Unicode)
byte[] utf8Bytes = Encoding.UTF8.GetBytes(text);
// Step 2: byte[] → Base64 string
string encoded = Convert.ToBase64String(utf8Bytes);
Console.WriteLine(encoded);
// Output: SGVsbG8g5LiW55WMIPCfkYs=
// One-liner
string result = Convert.ToBase64String(Encoding.UTF8.GetBytes(text));
Console.WriteLine(result);
// Decode back
byte[] decodedBytes = Convert.FromBase64String(encoded);
string decodedText = Encoding.UTF8.GetString(decodedBytes);
Console.WriteLine(decodedText); // Hello 世界 👋Safe Decoding with TryFromBase64String
using System;
using System.Text;
// Safe decode — returns false instead of throwing on invalid input
public static string? SafeDecode(string encoded)
{
// Pre-size the destination buffer: Base64 output is ~4/3 of input, so
// input.Length / 4 * 3 is an upper bound on decoded bytes.
Span<byte> buffer = new byte[encoded.Length];
if (!Convert.TryFromBase64String(encoded, buffer, out int bytesWritten))
{
return null; // invalid Base64
}
return Encoding.UTF8.GetString(buffer.Slice(0, bytesWritten));
}
// Usage
string? maybeText = SafeDecode(userInput);
if (maybeText is not null)
{
Console.WriteLine($"Decoded: {maybeText}");
}
else
{
Console.WriteLine("Not valid Base64");
}Encoding a File
using System;
using System.IO;
using System.Security.Cryptography;
using System.Threading.Tasks;
// Small files — load entire file into memory
public static string EncodeSmallFile(string path)
{
byte[] bytes = File.ReadAllBytes(path);
return Convert.ToBase64String(bytes);
}
// Async version for I/O-bound scenarios
public static async Task<string> EncodeSmallFileAsync(string path)
{
byte[] bytes = await File.ReadAllBytesAsync(path);
return Convert.ToBase64String(bytes);
}
// Large files — streaming with ToBase64Transform + CryptoStream
public static async Task EncodeLargeFileAsync(string inputPath, string outputPath)
{
await using var input = File.OpenRead(inputPath);
await using var output = File.Create(outputPath);
using var transform = new ToBase64Transform();
await using var cs = new CryptoStream(output, transform, CryptoStreamMode.Write);
await input.CopyToAsync(cs);
// FlushFinalBlock is called automatically when CryptoStream is disposed.
// No manual step needed — the using pattern handles it.
}Encoding an Image (System.Drawing / ImageSharp)
using System;
using System.IO;
// Encode any image file (JPEG, PNG, WebP, etc.)
public static string EncodeImage(string imagePath)
{
byte[] bytes = File.ReadAllBytes(imagePath);
return Convert.ToBase64String(bytes);
}
// Build a data URL for embedding in HTML/CSS
public static string BuildDataUrl(string imagePath)
{
string base64 = EncodeImage(imagePath);
string ext = Path.GetExtension(imagePath).TrimStart('.').ToLowerInvariant();
string mimeType = ext switch
{
"jpg" or "jpeg" => "image/jpeg",
"png" => "image/png",
"webp" => "image/webp",
"gif" => "image/gif",
"svg" => "image/svg+xml",
_ => "application/octet-stream"
};
return $"data:{mimeType};base64,{base64}";
}
// In-memory Bitmap → PNG bytes → Base64 (System.Drawing.Common on Windows)
// public static string BitmapToBase64(Bitmap image)
// {
// using var ms = new MemoryStream();
// image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
// return Convert.ToBase64String(ms.ToArray());
// }URL-Safe Base64 (JWT-Style)
using System;
using System.Text;
public static class Base64Url
{
// Encode → RFC 4648 §5 URL-safe with no padding (JWT format)
public static string Encode(byte[] bytes)
{
string std = Convert.ToBase64String(bytes);
return std
.TrimEnd('=') // remove padding
.Replace('+', '-') // + → -
.Replace('/', '_'); // / → _
}
public static string EncodeString(string text) =>
Encode(Encoding.UTF8.GetBytes(text));
// Decode → restore padding and standard alphabet, then FromBase64String
public static byte[] Decode(string urlSafe)
{
string s = urlSafe.Replace('-', '+').Replace('_', '/');
// Add padding back
int padding = (4 - s.Length % 4) % 4;
s += new string('=', padding);
return Convert.FromBase64String(s);
}
public static string DecodeString(string urlSafe) =>
Encoding.UTF8.GetString(Decode(urlSafe));
}
// Usage
var payload = "{\"user_id\":42,\"role\":\"admin\"}";
string jwtPart = Base64Url.EncodeString(payload);
Console.WriteLine(jwtPart); // eyJ1c2VyX2lkIjo0Miwicm9sZSI6ImFkbWluIn0
string roundTripped = Base64Url.DecodeString(jwtPart);
Console.WriteLine(roundTripped);Encoding a Class via JSON
using System;
using System.Text;
using System.Text.Json;
public record User(int Id, string Email, string[] Roles, string Joined);
public static class UserEncoder
{
private static readonly JsonSerializerOptions JsonOpts = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
public static string Encode(User user)
{
byte[] json = JsonSerializer.SerializeToUtf8Bytes(user, JsonOpts);
return Convert.ToBase64String(json);
}
public static User? Decode(string encoded)
{
byte[] json = Convert.FromBase64String(encoded);
return JsonSerializer.Deserialize<User>(json, JsonOpts);
}
}
var user = new User(42, "[email protected]",
new[] { "admin", "editor" }, "2026-01-15");
string encoded = UserEncoder.Encode(user);
Console.WriteLine(encoded);
User? roundTripped = UserEncoder.Decode(encoded);
Console.WriteLine(roundTripped);HTTP Basic Auth Header in C#
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
public static class BasicAuth
{
public static string BuildHeader(string user, string password)
{
string creds = $"{user}:{password}";
string token = Convert.ToBase64String(Encoding.UTF8.GetBytes(creds));
return $"Basic {token}";
}
// Using HttpClient with the built-in AuthenticationHeaderValue
public static async Task<string> CallProtectedApiAsync(string user, string password)
{
using var client = new HttpClient();
string token = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{user}:{password}"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", token);
return await client.GetStringAsync("https://api.example.com/protected");
}
}Zero-Allocation Encoding with Span (.NET 6+)
using System;
using System.Buffers;
using System.Buffers.Text;
// For hot paths — avoid allocating strings by encoding into a stack-alloc'd span
public static void EncodeToSpan(ReadOnlySpan<byte> input)
{
// Base64 output size formula: ceil(inputLen / 3) * 4
int outputLen = ((input.Length + 2) / 3) * 4;
Span<char> buffer = outputLen <= 512
? stackalloc char[outputLen] // stack for small inputs
: new char[outputLen]; // heap for larger
if (Convert.TryToBase64Chars(input, buffer, out int charsWritten))
{
// Use the span directly — pass to another API that accepts ReadOnlySpan<char>
// e.g. Console.Out.Write(buffer[..charsWritten]);
Console.WriteLine(new string(buffer[..charsWritten]));
}
}
// System.Buffers.Text.Base64 — raw byte-level API for pipelines
public static void EncodeWithBase64Buffer(ReadOnlySpan<byte> input, Span<byte> output)
{
OperationStatus status = Base64.EncodeToUtf8(input, output,
out int bytesConsumed, out int bytesWritten);
if (status != OperationStatus.Done)
{
throw new InvalidOperationException("Base64 encoding failed: " + status);
}
}Common Pitfalls in C# Base64 Code
- Using Encoding.ASCII or Encoding.Default — Silently loses non-ASCII characters (emoji, accented letters, CJK). Always use
Encoding.UTF8unless you have a specific reason. Encoding.Default varies by OS locale, making bugs OS-dependent. - Ignoring FormatException on decode —
Convert.FromBase64Stringthrows on invalid input (wrong alphabet, corrupt padding, non-Base64 chars). Wrap in try/catch, or preferConvert.TryFromBase64Stringwhich returns false instead of throwing. - Using ToBase64String for JWT payloads — Standard Base64 contains
+,/, and=, all of which get percent-encoded in URLs and rejected by JWT verifiers. Use the Base64Url helper class or WebEncoders.Base64UrlEncode from Microsoft.AspNetCore.WebUtilities. - Manual line-breaking — Splitting long Base64 strings by hand for MIME email is error-prone. Pass
Base64FormattingOptions.InsertLineBreaksas the second argument toConvert.ToBase64String— .NET inserts RFC 2045-compliant CR-LF every 76 chars. - Loading huge files with ReadAllBytes — File.ReadAllBytes loads the entire file into memory. For files over 100MB, use
CryptoStream + ToBase64Transformto stream — memory usage stays bounded regardless of file size.
Command Line Alternative
For quick one-offs from a terminal, use dotnet-script or PowerShell:
# PowerShell (built-in, no install)
[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("Hello"))
# Output: SGVsbG8=
# dotnet-script (install: dotnet tool install -g dotnet-script)
dotnet script eval 'Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("Hello"))'
# Or C# top-level statements (dotnet run works on any .cs file with just statements)
echo 'System.Console.WriteLine(System.Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("Hello")));' > /tmp/b64.cs
dotnet run /tmp/b64.cs
# System base64 CLI (independent of .NET)
echo -n "Hello" | base64Key Facts
- Namespace:
- System.Convert (built into all .NET flavors — no NuGet needed)
- Standard encoding:
- Convert.ToBase64String(byte[])
- Standard decoding:
- Convert.FromBase64String(string) — throws FormatException on invalid input
- Safe decoding:
- Convert.TryFromBase64String(string, Span, out int) — returns false, no throw
- MIME line breaks:
- Convert.ToBase64String(bytes, Base64FormattingOptions.InsertLineBreaks)
- URL-safe:
- Manual (helper above) or WebEncoders.Base64UrlEncode in ASP.NET Core
- Streaming:
- CryptoStream + ToBase64Transform (System.Security.Cryptography)
- Zero-alloc (.NET 6+):
- Convert.TryToBase64Chars(Span, Span, out int)
Related Base64 Tools
- Base64 Encode Online — general-purpose browser encoder
- Base64 Encode in Python — Python 3 equivalent
- Base64 Encode in JavaScript — Node.js and browser
- Base64 Encode in Java — java.util.Base64
- Base64 Encode in Go — Go encoding/base64
- URL-Safe Base64 — cross-language URL encoding
- JWT Debugger — inspect JWT tokens