28

I'm working on a quick captcha generator for a simple site I'm putting together, and I'm hoping to pass an encrypted key in the url of the page. I could probably do this as a query string parameter easy enough, but I'm hoping not too (just because nothing else runs off the query string)...

My encryption code produces a byte[], which is then transformed using Convert.ToBase64String(byte[]) into a string. This string, however, is still not quite url friendly, as it can contain things like '/' and '='. Does anyone know of a better function in the .NET framework to convert a byte array to a url friendly string?

I know all about System.Web.HttpUtility.UrlEncode() and its equivalents, however, they only work properly with query string parameters. If I url encode an '=' inside of the path, my web server brings back a 400 Bad Request error.

Anyways, not a critical issue, but hoping someone can give me a nice solution

**EDIT: Just to be absolutely sure exactly what I'm doing with the string, I figured I would supply a little more information.

The byte[] that results from my encryption algorithm should be fed through some sort of algorithm to make it into a url friendly string. After this, it becomes the content of an XElement, which is then used as the source document for an XSLT transformation, and is used as a part of the href attribute for an anchor. I don't believe the xslt transformation is causing the issues, since what is coming through on the path appears to be an encoded query string parameter, but causes the HTTP 400

I've also tried HttpUtility.UrlPathEncode() on a base64 string, but that doesn't seem to do the trick either (I still end up with '/'s in my url)**

LorenVS
  • 12,597
  • 10
  • 47
  • 54

4 Answers4

89

You're looking for HttpServerUtility.UrlTokenEncode and HttpServerUtility.UrlTokenDecode, in System.Web.

They encode in base64, replacing the potentially dangerous '+' and '/' chars with '-' and '_' instead.

MSDN documentation

Pavel Chuchuva
  • 22,633
  • 10
  • 99
  • 115
orip
  • 73,323
  • 21
  • 116
  • 148
  • 1
    +1: Base64 is a more compact encoding than just using the hex digits from `BitConverter.ToString`. – LukeH Dec 11 '09 at 09:42
  • 2
    Worked like a charm... Wicked (little known) function there... Thanks man – LorenVS Dec 11 '09 at 10:00
  • @LorenVS - sure. I didn't know it was there either, until we stumbled on it at work and replaced our own implementation with it. – orip Dec 11 '09 at 11:06
  • Awesome! Fixed an issue I was having with Convert.ToBase64String and Convert.FromBase64String when transmitted in URL. – Ales Potocnik Hahonina Nov 09 '12 at 10:12
  • 3
    How annoying it is that Microsoft chose to place this in the `System.Web` assembly. Sigh. – Asbjørn Ulsberg Jun 07 '17 at 09:00
  • @AsbjørnUlsberg, Kind of agree but does make sense since it is a domain specific version. I can imagine other versions to deal with other issues, e.g. User input: so avoid ['0', 'i' and 'l']. I think it would be handy if ToBase64String() would take a parameter allowing for a custom set of characters. – AnthonyVO Sep 17 '18 at 18:49
  • 2
    **Use with caution** as UrlTokenEncode adds a number at the end which indicates the number of = signs that were removed. See a possible solution here [link](https://codeday.me/en/qa/20190316/14910.html) – Yair Zamir May 12 '19 at 13:42
4

For ASP.NET Core 6.0+ use Microsoft.AspNetCore.WebUtilities.WebEncoders:

byte[] bytes = RandomNumberGenerator.GetBytes(64);

string encoded = WebEncoders.Base64UrlEncode(bytes);
byte[] decoded = WebEncoders.Base64UrlDecode(encoded);
Alex Narbut
  • 126
  • 1
  • 3
0

Have a look at System.BitConverter.ToString(myByteArray)

Handy for one way encoding for things like hashes but as pointed out by ssg it's not very efficient. I wouldn't recommend it for large amounts of data.

David Glenn
  • 24,412
  • 19
  • 74
  • 94
0

HttpServerUtility.UrlTokenEncode and similar are no longer available in .NET Core as of .NET 6. The following method should provide an RFC4648§5-compliant base64 string based on a quick reading.

This code allocates at least four objects on the heap, so ultra-high performance use cases should tune it a bit and perhaps wait for .NET 7, which is expected to introduce a means to get the random bytes into a Span for stack allocation.

private string GetUrlSafeRandomBytes(int byteCount)
{
    var salt = new byte[byteCount];
    using var rng = RandomNumberGenerator.Create();
    rng.GetBytes(salt);
    var asBase64 = Convert.ToBase64String(salt);
    var asUrlSafeString = asBase64.Replace('+', '-').Replace('/', '_');
    return asUrlSafeString;
}
Charles Burns
  • 10,310
  • 7
  • 64
  • 81