124

I want to base64 encode data to put it in a URL and then decode it within my HttpHandler.

I have found that Base64 Encoding allows for a '/' character which will mess up my UriTemplate matching. Then I found that there is a concept of a "modified Base64 for URL" from wikipedia:

A modified Base64 for URL variant exists, where no padding '=' will be used, and the '+' and '/' characters of standard Base64 are respectively replaced by '-' and '_', so that using URL encoders/decoders is no longer necessary and has no impact on the length of the encoded value, leaving the same encoded form intact for use in relational databases, web forms, and object identifiers in general.

Using .NET I want to modify my current code from doing basic base64 encoding and decoding to using the "modified base64 for URL" method. Has anyone done this?

To decode, I know it starts out with something like:

string base64EncodedText = base64UrlEncodedText.Replace('-', '+').Replace('_', '/');

// Append '=' char(s) if necessary - how best to do this?

// My normal base64 decoding now uses encodedText

But, I need to potentially add one or two '=' chars to the end which looks a little more complex.

My encoding logic should be a little simpler:

// Perform normal base64 encoding
byte[] encodedBytes = Encoding.UTF8.GetBytes(unencodedText);
string base64EncodedText = Convert.ToBase64String(encodedBytes);

// Apply URL variant
string base64UrlEncodedText = base64EncodedText.Replace("=", String.Empty).Replace('+', '-').Replace('/', '_');

I have seen the Guid to Base64 for URL StackOverflow entry, but that has a known length and therefore they can hardcode the number of equal signs needed at the end.

Fredrik Haglund
  • 2,578
  • 2
  • 17
  • 19
Kirk Liemohn
  • 7,733
  • 9
  • 46
  • 57

5 Answers5

181

Also check class HttpServerUtility with UrlTokenEncode and UrlTokenDecode methods that is handling URL safe Base64 encoding and decoding.

Note 1: The result is not a valid Base64 string. Some unsafe characters for URL are replaced.

Note 2: The result differs from the base64url algorithm in RFC4648, it replaces the '=' padding with '0', '1' or '2' depending on how many equal signs it replaced to make the value safe for a query parameter.

///<summary>
/// Base 64 Encoding with URL and Filename Safe Alphabet using UTF-8 character set.
///</summary>
///<param name="str">The origianl string</param>
///<returns>The Base64 encoded string</returns>
public static string Base64ForUrlEncode(string str)
{
    byte[] encbuff = Encoding.UTF8.GetBytes(str);
    return HttpServerUtility.UrlTokenEncode(encbuff);
}
///<summary>
/// Decode Base64 encoded string with URL and Filename Safe Alphabet using UTF-8.
///</summary>
///<param name="str">Base64 code</param>
///<returns>The decoded string.</returns>
public static string Base64ForUrlDecode(string str)
{
    byte[] decbuff = HttpServerUtility.UrlTokenDecode(str);
    return Encoding.UTF8.GetString(decbuff);
}
Fredrik Haglund
  • 2,578
  • 2
  • 17
  • 19
  • Won't this use % encoding for every / + and = ? This is not as efficient as the other answer – JoelFan Jun 01 '12 at 19:39
  • No, it replaces equal signs used for padding in the end with a number and substitutes plus and slash with minus and underscore. – Fredrik Haglund Jun 17 '12 at 00:11
  • 16
    Note that `UrlTokenEncode` is not strictly [base64url](http://tools.ietf.org/html/rfc4648#section-5), as it replaces the '=' padding with '0', '1' or '2' depending on how many equal signs it replaced. – ladenedge Aug 14 '12 at 16:18
84

This ought to pad it out correctly:-

 base64 = base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '=');
AnthonyWJones
  • 187,081
  • 35
  • 232
  • 306
  • 4
    Won't this add up to three '=' chars? It appears that there will only be 0, 1, or 2 of these. – Kirk Liemohn Aug 04 '09 at 17:10
  • 1
    @Kirk: If it adds 3 characters then the base64 string is already corrupt. I guess it would be a good idea to validate the string, it should only contain the characters expected and Length % 4 != 3. – AnthonyWJones Aug 04 '09 at 17:36
  • Hmmm. In trying this out, it isn't doing the trick. Still looking for answers. The number of equal signs just isn't panning out properly. – Kirk Liemohn Aug 04 '09 at 19:31
  • Oops needed to invert the modulo. – AnthonyWJones Aug 04 '09 at 21:37
  • in this example, what is base64? If it's a System.String, then I'm not sure how this would work, because what you end up with depends on the encoding used to get the bytes. In this particular example, you might get away with it because he's using UTF8, but with Unicode this won't work. – Ben Collins Jul 14 '11 at 15:04
  • @Ben: Yes the variable `base64` in this example is of type string. I think you may be a little confused though, at what point is Unicode a problem? Bear in mind the sample code above is purely to include the appropriate padding of "=" to the end of the string to ensure it can be decoded correctly. – AnthonyWJones Jul 15 '11 at 08:17
  • @AnthonyWJones: Unicode (or any multi-byte encoding) is a problem because you would be padding the wrong number of bytes. You can tell this by inspection, but I discovered it after trying your solution. I could not make the above work when I was using Encoding.Unicode to get the bytes. To solve the problem in an encoding-agnostic way, you have to pad the array of bytes you feed to Convert.ToBase64String, rather than the original string. – Ben Collins Jul 15 '11 at 19:21
  • @Ben: My padding code occurs __after__ a) the original string has been converted to bytes and b) after those bytes have been converted to a form of Base64. This is indicated by the input variable being called "base64" and the nature of the question. In reality this question boils down to "How do I pad a string with a specific character to make its character length a multiple of a specified integer". It just so happens that the context is using "=" to pad a base64-esq string to multiples of 4. – AnthonyWJones Jul 18 '11 at 12:07
  • @AnthonyWJones Ok, thanks for the clarification. It wasn't clear to me until now that you were padding an already-encoded string. The problem I was working on was the one of padding a string before encoding so as to avoid having '=' characters in the encoded result. – Ben Collins Jul 18 '11 at 13:15
  • 3
    @AnthonyWJones 'it should only contain the characters expected and Length % 4 != **1**', right? – blueling Sep 27 '13 at 12:30
38

Not enough points to comment, but in case it helps, the code snippet that Sushil found in the link provided RFC 7515 JSON Web Signature works for when encoding Base 64 as a parameter in URL.

Copied snippet below for those that are lazy:

    static string Base64UrlEncode(byte[] arg)
    {
        string s = Convert.ToBase64String(arg); // Regular base64 encoder
        s = s.Split('=')[0]; // Remove any trailing '='s
        s = s.Replace('+', '-'); // 62nd char of encoding
        s = s.Replace('/', '_'); // 63rd char of encoding
        return s;
    }

    static byte[] Base64UrlDecode(string arg)
    {
        string s = arg;
        s = s.Replace('-', '+'); // 62nd char of encoding
        s = s.Replace('_', '/'); // 63rd char of encoding
        switch (s.Length % 4) // Pad with trailing '='s
        {
            case 0: break; // No pad chars in this case
            case 2: s += "=="; break; // Two pad chars
            case 3: s += "="; break; // One pad char
            default: throw new System.Exception(
              "Illegal base64url string!");
        }
        return Convert.FromBase64String(s); // Standard base64 decoder
    }
Scott
  • 4,458
  • 1
  • 19
  • 27
Stefan Zvonar
  • 3,959
  • 3
  • 24
  • 30
21

I hit here while looking for code to do encode/decode for base64url encoding which is little different than base64 as explained in the question.

Found c# code snippet in this document. JSON Web Signature ietf draft

Community
  • 1
  • 1
Sushil
  • 5,265
  • 2
  • 17
  • 15
  • 2
    This was the only solution that worked for me when parsing a message in the GMail API v1 (Message.Raw) – HeyZiko Apr 18 '15 at 18:22
6

In comparison to the accepted answer, here is how you would fundamentally decode a base64 encoded url, using C#:

Decode:

string codedValue = "base64encodedUrlHere";

string decoded;
byte[] buffer =  Convert.FromBase64String(codedValue);
decoded = Encoding.UTF8.GetString(buffer);
Chris Halcrow
  • 28,994
  • 18
  • 176
  • 206