How to achieve Base64 URL safe encoding in C#?
Asked Answered
B

10

170

I want to achieve Base64 URL safe encoding in C#. In Java, we have the common Codec library which gives me an URL safe encoded string. How can I achieve the same using C#?

byte[] toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII.GetBytes("StringToEncode");
string returnValue = System.Convert.ToBase64String(toEncodeAsBytes);

The above code converts it to Base64, but it pads ==. Is there is way to achieve URL safe encoding?

Binate answered 14/10, 2014 at 6:6 Comment(6)
Can't you just use Url.Encode on string in BASE64?Alyciaalyda
In which namespace Url class is present in c#?Binate
Take a look: msdn.microsoft.com/en-us/library/… You need to reference System.Web assembly.Alyciaalyda
It's converting = into %3D. I dont want that.Binate
So what you mean by url safe? %3D is url safe.Alyciaalyda
Duplicate of #1229201Clamp
K
258

It is common to simply swap alphabet for use in urls, so that no %-encoding is necessary; only 3 of the 65 characters are problematic - +, / and =. the most common replacements are - in place of + and _ in place of /. As for the padding: just remove it (the =); you can infer the amount of padding needed. At the other end: just reverse the process:

string returnValue = System.Convert.ToBase64String(toEncodeAsBytes)
        .TrimEnd(padding).Replace('+', '-').Replace('/', '_');

with:

static readonly char[] padding = { '=' };

and to reverse:

string incoming = returnValue
    .Replace('_', '/').Replace('-', '+');
switch(returnValue.Length % 4) {
    case 2: incoming += "=="; break;
    case 3: incoming += "="; break;
}
byte[] bytes = Convert.FromBase64String(incoming);
string originalText = Encoding.ASCII.GetString(bytes);

The interesting question, however, is: is this the same approach that the "common codec library" uses? It would certainly be a reasonable first thing to test - this is a pretty common approach.

Kinghorn answered 14/10, 2014 at 7:10 Comment(8)
In Common Codec they are using [0-9a-zA-Z_-] Character for url safe mode.Binate
this is also mentioned in the wiki page for Base64 under URL applications. en.wikipedia.org/wiki/Base64Beatup
You also have this function '#1887186' which does all the hard work for you.Pooch
Thanks a lot Marc; I was trying to decode a string created by Java's encodeBase64URLSafeString and in the end the issue was the missing padding on the end of the string that you solved with the switch statement!Silvanasilvano
Thanks for noting these characters. When interfacing with different languages some functions do not return same results when encoding with encodeBASE64URL. I am using PC Soft's WinDev v22 and it handles the problematic chars but the vendor I am working with what ever they are using does not... so you our HMAC_SHA_256 signatures where not matching and I found the issue in the encoding!Burford
Just for search-findability... this is a great solution for decoding those pesky Google Gmail API bodies.Faulty
Why do we not need: case 1: incoming += "==="; break; ?Vidar
@alexdafranca because it will never have a length mod 4 of 1. 3x8 bits become 4x6 bits (and each 6 bits is one of 64 characters in the chosen alphabet), 0x8 bits is encoded as 0x6 bits without padding, 1x8 bits is encoded as 2x6 bits with == padding, 2x8 is encoded as 3x6 with = padding, 3x8 is encoded as 4x6 without padding and then it is aligned so it repeats. Nothing is ever encoded to 1x6 bits, so you never need === padding.Bikaner
L
159

You can use class Base64UrlEncoder from namespace Microsoft.IdentityModel.Tokens.

const string StringToEncode = "He=llo+Wo/rld";

var encodedStr = Base64UrlEncoder.Encode(StringToEncode);
var decodedStr = Base64UrlEncoder.Decode(encodedStr);

if (decodedStr == StringToEncode)
    Console.WriteLine("It works!");
else
    Console.WriteLine("Dangit!");

Microsoft.IdentityModel.Tokens is a NuGet package that has to be downloaded.

Levasseur answered 30/9, 2016 at 12:29 Comment(3)
This is much cleaner than the accepted answer. Any downside ?Imbecility
I'm assuming by the name that this isn't cross platform. Is that the case?Riehl
@BrandonS. Can confirm that it is cross platform. Generating url encoded base 64 string encoded signature that is handled correctly on google GKEWeek
Z
53

Another option, if you are using ASP.NET Core, would be to use Microsoft.AspNetCore.WebUtilities.WebEncoders.Base64UrlEncode.

If you are not using ASP.NET Core, the WebEncoders source is available under the Apache 2.0 License.

Zymogenesis answered 19/2, 2018 at 3:0 Comment(0)
T
12

Based off the answers here with some performance improvements, we've published a very easy to use url-safe base64 implementation to NuGet with the source code available on GitHub (MIT licensed).

Usage is as easy as

var bytes = Encoding.UTF8.GetBytes("Foo");
var encoded = UrlBase64.Encode(bytes);
var decoded = UrlBase64.Decode(encoded);
Transmittal answered 17/7, 2017 at 18:28 Comment(3)
Amazing thank you. Out of interest why have you opted for "string".Replace for the encode method, but a loop with manual replaces for the decode?Conformable
@ᴍᴀᴛᴛʙᴀᴋᴇʀ I need to revisit it and run some benchmarks, but it's because we add to the latter so it's represented by a growable list of characters instead of an immutable string.Transmittal
Another class with return type = string instead : github.com/vndevpro/architecture-common/blob/master/…Inclose
M
9

To get an URL-safe base64-like encoding, but not "base64url" according to RFC4648, use System.Web.HttpServerUtility.UrlTokenEncode(bytes) to encode, and System.Web.HttpServerUtility.UrlTokenDecode(bytes) to decode.

Merari answered 7/10, 2015 at 17:47 Comment(1)
This doesn't provide a standards conform URL-safe Base64 encoding according to RFC4648. See also this Q&A. Use with caution.Tereus
N
2

Simplest solution: (with no padding)

private static string Base64UrlEncode(string input) {
    var inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
    // Special "url-safe" base64 encode.
    return Convert.ToBase64String(inputBytes)
      .Replace('+', '-') // replace URL unsafe characters with safe ones
      .Replace('/', '_') // replace URL unsafe characters with safe ones
      .Replace("=", ""); // no padding
  }

Credit goes to: Tholle

Notochord answered 9/1, 2020 at 9:26 Comment(0)
I
2
    public string Decode(string str)
    {
       byte[] decbuff = Convert.FromBase64String(str.Replace(",", "=").Replace("-", "+").Replace("/", "_"));
       return System.Text.Encoding.UTF8.GetString(decbuff);
    }
    
    public string Encode(string input)
    {
        byte[] encbuff = Encoding.UTF8.GetBytes(input ?? "");
        return Convert.ToBase64String(encbuff).Replace("=", ",").Replace("+", "-").Replace("_", "/");
    }

This is the way to do it to align with JavaScript!

Irritant answered 31/12, 2020 at 22:51 Comment(1)
Just based on the other answers, have you got that last Replace right? The other answers have .Replace("/", "_")Panjabi
L
0

Here is another method to decode an url-safe base64 was encode by same way with Marc. I just don't get why 4-length%4 worked(it does).

As follows, only the origin's bit length are common multiple of 6 and 8, base64 do not append "=" to result.

1 2 3 4 5 6 7 8|1 2 3 4 5 6 7 8|1 2 3 4 5 6 7 8 
1 2 3 4 5 6|1 2 3 4 5 6|1 2 3 4 5 6|1 2 3 4 5 6
                "=="            "="

So we can do it conversely, if result's bit length can't divisible by 8, it has been appended:

base64String = base64String.Replace("-", "+").Replace("_", "/");
var base64 = Encoding.ASCII.GetBytes(base64String);
var padding = base64.Length * 3 % 4;//(base64.Length*6 % 8)/2
if (padding != 0)
{
    base64String = base64String.PadRight(base64String.Length + padding, '=');
}
return Convert.FromBase64String(base64String);
Lakitalaks answered 4/1, 2015 at 11:0 Comment(0)
D
0

Karanvir Kang's answer is a good one and I voted for it. However, it does leave an odd character on the end of the string (indicating the number of padding characters removed). Here is my solution.

var bytesToEncode = System.Text.Encoding.UTF8.GetBytes("StringToEncode"); 
var bytesEncodedPadded = HttpServerUtility.UrlTokenEncode(bytesToEncode);
var objectIdBase64 = bytesEncodedPadded.Substring(0, bytesEncodedPadded.Length - 1);
Deanery answered 1/12, 2019 at 0:55 Comment(0)
S
-2

Using Microsoft cryptographic engine in UWP.

uint length = 32;

IBuffer buffer = CryptographicBuffer.GenerateRandom(length);
string base64Str = CryptographicBuffer.EncodeToBase64String(buffer)
                   // ensure url safe
                   .TrimEnd('=').Replace('+', '-').Replace('/', '_');

return base64Str;
Syndicate answered 1/5, 2018 at 14:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.