Code for decoding/encoding a modified base64 URL (in ASP.NET Framework)
Asked Answered
A

5

125

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.

Adiana answered 4/8, 2009 at 16:58 Comment(0)
G
84

This ought to pad it out correctly:-

 base64 = base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '=');
Gallo answered 4/8, 2009 at 17:6 Comment(10)
Won't this add up to three '=' chars? It appears that there will only be 0, 1, or 2 of these.Adiana
@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.Gallo
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.Adiana
Oops needed to invert the modulo.Gallo
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.Sardonic
@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.Gallo
@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.Sardonic
@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.Gallo
@Gallo 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.Sardonic
@Gallo 'it should only contain the characters expected and Length % 4 != 1', right?Heavyduty
C
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);
}
Curvature answered 24/11, 2009 at 10:26 Comment(3)
Won't this use % encoding for every / + and = ? This is not as efficient as the other answerLissie
No, it replaces equal signs used for padding in the end with a number and substitutes plus and slash with minus and underscore.Curvature
Note that UrlTokenEncode is not strictly base64url, as it replaces the '=' padding with '0', '1' or '2' depending on how many equal signs it replaced.Choe
G
84

This ought to pad it out correctly:-

 base64 = base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '=');
Gallo answered 4/8, 2009 at 17:6 Comment(10)
Won't this add up to three '=' chars? It appears that there will only be 0, 1, or 2 of these.Adiana
@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.Gallo
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.Adiana
Oops needed to invert the modulo.Gallo
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.Sardonic
@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.Gallo
@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.Sardonic
@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.Gallo
@Gallo 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.Sardonic
@Gallo 'it should only contain the characters expected and Length % 4 != 1', right?Heavyduty
M
40

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
    }
Misguidance answered 13/10, 2015 at 22:40 Comment(2)
this is compatible with Xamarin for not using System.WebGarble
Exactly what I was looking for! Really good option for Xamarin without having to pull in a library.Headachy
S
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

Scyphus answered 16/1, 2013 at 8:23 Comment(1)
This was the only solution that worked for me when parsing a message in the GMail API v1 (Message.Raw)Medwin
H
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);
Hypnosis answered 7/1, 2016 at 1:40 Comment(2)
maybe if you provide more details and comparison to the accepted answer you might get an upvote - thanksRiddance
It's not a base64-encoded URL, but base64url-encoded data.Searcy

© 2022 - 2024 — McMap. All rights reserved.