Encode and decode a string
Convert works with bytes, so pick an encoding explicitly. For anything that leaves your process, that is almost always 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 is UTF-16LE and doubles the size of ASCII text. It is the right choice only when the other side expects UTF-16, for example the PowerShell encoded command format.
A common real use is the HTTP Basic Auth header:
string token = Convert.ToBase64String(Encoding.UTF8.GetBytes("user:pass"));
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", token);
// Authorization: Basic dXNlcjpwYXNz
The Basic Auth header generator produces the same value in the browser.
URL-safe Base64: Base64Url in .NET 9
.NET 9 added System.Buffers.Text.Base64Url. It encodes without padding and decodes with or without it:
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=="); // <<???>>
It rejects the standard alphabet: Base64Url.DecodeFromChars("PDw/Pz8+Pg") throws FormatException. There are also span-based overloads (EncodeToChars, DecodeFromUtf8, TryDecodeFromChars) that avoid allocations.
On older runtimes without the Microsoft.Bcl.Memory package, convert manually:
string url = Convert.ToBase64String(d)
.TrimEnd('=').Replace('+', '-').Replace('/', '_'); // PDw_Pz8-Pg
Decoding needs the reverse replacements plus padding to a multiple of 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")); // <<???>>
ASP.NET Core projects can use WebEncoders.Base64UrlEncode from Microsoft.AspNetCore.WebUtilities instead. More on the two alphabets in Base64 vs Base64URL.
Validating input without exceptions
Convert.FromBase64String throws System.FormatException on bad input. On .NET 9 the message is the same for almost every case:
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.
We got that message for PDw_Pz8-Pg== (URL-safe), aGk=!! (junk), aGk (no padding), a and aGk==. It accepts whitespace (" aGk= " and "aGVs\nbG8K" decode fine) and non-zero padding bits (aGl=).
To avoid exceptions in hot paths, use the Try variant with a buffer, or 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
The buffer must be large enough for the decoded output, or TryFromBase64String returns false even for valid input. Size it as (s.Length * 3) / 4. The Base64 validator and the errors guide help with strings that fail.
Line breaks (MIME)
Base64FormattingOptions.InsertLineBreaks wraps output at 76 characters with \r\n, without a trailing newline:
string mime = Convert.ToBase64String(
Encoding.ASCII.GetBytes(new string('x', 60)),
Base64FormattingOptions.InsertLineBreaks);
// "eHh4eHh4...eHh4" (76 chars) + "\r\n" + "eHh4"
FromBase64String accepts the wrapped form back without changes. The MIME Base64 encoder produces the same layout.
Encode and decode a file
Console.WriteLine(Convert.ToBase64String(File.ReadAllBytes("hello.txt"))); // aGVsbG8K
File.WriteAllBytes("copy.txt", Convert.FromBase64String("aGVsbG8K"));
Large files with CryptoStream
ToBase64Transform and FromBase64Transform plug into a CryptoStream, which keeps memory flat. Despite the namespace, no cryptography is involved:
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);
}
Disposing the CryptoStream flushes the final block and padding; skipping it truncates the output. On a 300 KB test file both directions matched the in-memory result. For a quick manual check, open the result with Base64 to file.
Common pitfalls
- Data URI prefixes.
data:image/png;base64,contains:and;, which triggerFormatException. Strip everything up to the comma. - URL-safe tokens in
Convert. JWT parts fail with the generic message above. UseBase64Urlor the JWT decoder. Encoding.Default. On .NET Framework it is the ANSI code page; on .NET Core and later it is UTF-8. Code moved between the two changes output for non-ASCII text.- Base64 is not encryption. Connection strings “protected” with Base64 are plain text to anyone. See encoding vs encryption.
Version notes
- .NET Framework 1.0+:
Convert.ToBase64String,FromBase64String,ToBase64Transform. - .NET Core 2.1+: span overloads
Convert.TryFromBase64String,TryToBase64Charsand the UTF-8System.Buffers.Text.Base64class. - .NET 8:
Base64.IsValid. - .NET 9:
Base64Url, also available for older targets throughMicrosoft.Bcl.Memory.
FAQ
How do I fix 'The input is not a valid Base-64 string'?
Check for URL-safe characters (-, _), missing padding and data URI prefixes. Convert.FromBase64String accepts only the standard alphabet with correct padding. For tokens, use Base64Url.DecodeFromChars on .NET 9, which accepts both padded and unpadded input.
Can I use Base64Url on .NET 8 or .NET Framework?
Base64Url is built into .NET 9 and later. For older targets, Microsoft ships the same type in the Microsoft.Bcl.Memory package. Without it, convert the standard output with TrimEnd('=') and two Replace calls.
Why is Encoding.Unicode wrong for Base64 of text?
Encoding.Unicode is UTF-16LE, so hi becomes aABpAA== instead of aGk=. Use Encoding.UTF8 unless the receiver expects UTF-16, as PowerShell’s -EncodedCommand does.