Click here to Skip to main content
Click here to Skip to main content

Tagged as

Base32, base32url, base64url, and z-base-32 encoding and decoding in .net

, 1 May 2010
Rate this:
Please Sign up or sign in to vote.
Base32Url and Base64Url (2.5 KB)Standards based implementations of various Base32 and Base64 encoding/decoding methods. These are designed to encode binary data to plain text, and decode the resulting text back to the original binary. Useful when you need to transfer binary data through...
 
Standards based implementations of various Base32 and Base64 encoding/decoding methods. These are designed to encode binary data to plain text, and decode the resulting text back to the original binary. Useful when you need to transfer binary data through technologies that only support text (such as including binary security tokens in URLs).
 
Base32Url encodes with only the characters A to Z and 2 to 7. No hyphens, underscores, pluses, slashes or equals are used, making it usable as a URL token in almost all circumstances. Base32Url also supports custom alphabets. A custom case sensitive alphabet with only consonant (non vowel) characters can be used to ensure your tokens do not contain accidental profanities. The following is an example that avoids vowels, the letter L and has no numeric characters: BCDFGHKMNPQRSTVWXYZbcdfghkmnpqrs.
 
Base64Url is more compact than Base32Url and it is almost always usable as a URL token or file-name. The only non alpha-numeric characters Base64Url contains are the hyphen (-) and underscore (_) characters, neither of these need further encoding for use in URLs or file-names.
 

Base32Url (Encoder / Decoder)

The default mode for the Base32 encoder/decoder is Base32Url. This uses the standard Base32 alphabet encoding but omits padding characters and is case insensitive.
Supports standard Base32 with padding characters (=) per Base32 from RFC 4648.
Supports the Base32 extension / alternate alphabet z-base-32
 

Base64Url (Encoder / Decoder)

Based on the standard .net Base64 encoder
Uses the URL-Safe alternative Base64 alphabet from RFC 4648
This is not the same as Microsoft’s HttpServerUtility.UrlTokenEncode.
 

Further Information and Usage

There are other implementations of base32 encoding out there but I feel the code of this base32 implementation is much simpler (far less code involved in the bit shifting calculations).
 
The base64 implementation I have here is a little hackish, but a far better option than the one you get from Microsoft.
 
The result you get from HttpServerUtility.UrlTokenEncode is essentially base64url, but instead of truncating the padding, they append a digit (of 0, 1 or 2) indicating the number of padding characters removed.
 
Usage:
 
Base32Url.ToBase32String(Encoding.ASCII.GetBytes("Hello World!"));
 
JBSWY3DPEBLW64TMMQQQ
 
var b32 = new Base32Url(true); // Base32Url(bool usePadding)
b32.Encode(Encoding.ASCII.GetBytes("Hello World!"));
 
JBSWY3DPEBLW64TMMQQQ====
 
For more information about the standards involved please see rfc 4648 http://tools.ietf.org/html/rfc4648[^]
 
Wikipedia also has good information. Please see http://en.wikipedia.org/wiki/Base32[^] and or http://en.wikipedia.org/wiki/Base64[^]
 
Written by Mhano Harkness - http://mhano.com[^]
 

Base32Url

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace MhanoHarkness
{
	/// <summary>
	/// Base32Url is a standard base 32 encoder/decoder except that padding turned
	/// off and it is not case sensitive (by default).
	/// 
	/// If you turn padding and case sensitivity on it becomes a standard base32
	/// encoder/decoder giving you 8 character chunks right padded with equals symbols.
	/// 
	/// If you leave padding off and use Base32Url.ZBase32Alphabet you
	/// get a z-base-32 compatible encoder/decoder.
	/// 
	/// See http://tools.ietf.org/html/rfc4648
	/// For more information see http://en.wikipedia.org/wiki/Base32
	/// </summary>
	public class Base32Url
	{
		public const char StandardPaddingChar = '=';
		public const string Base32StandardAlphabet  = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
		public const string ZBase32Alphabet = "ybndrfg8ejkmcpqxot1uwisza345h769";
		public char PaddingChar;
		public bool UsePadding;
		public bool IsCaseSensitive;
		public bool IgnoreWhiteSpaceWhenDecoding;
		private readonly string _alphabet;
		private Dictionary<string, uint> _index;
		// alphabets may be used with varying case sensitivity, thus index must not ignore case
		private static Dictionary<string, Dictionary<string, uint>> _indexes = new Dictionary<string, Dictionary<string, uint>>(2, StringComparer.InvariantCulture);
		/// <summary>
		/// Create case insensitive encoder/decoder using the standard base32 alphabet without padding.
		/// White space is not permitted when decoding (not ignored).
		/// </summary>
		public Base32Url() : this(false, false, false, Base32StandardAlphabet) { }
		/// <summary>
		/// Create case insensitive encoder/decoder using the standard base32 alphabet.
		/// White space is not permitted when decoding (not ignored).
		/// </summary>
		/// <param name="padding">Require/use padding characters?</param>
		public Base32Url(bool padding) : this(padding, false, false, Base32StandardAlphabet) { }
		/// <summary>
		/// Create encoder/decoder using the standard base32 alphabet.
		/// White space is not permitted when decoding (not ignored).
		/// </summary>
		/// <param name="padding">Require/use padding characters?</param>
		/// <param name="caseSensitive">Be case sensitive when decoding?</param>
		public Base32Url(bool padding, bool caseSensitive) : this(padding, caseSensitive, false, Base32StandardAlphabet) { }
		/// <summary>
		/// Create encoder/decoder using the standard base32 alphabet.
		/// </summary>
		/// <param name="padding">Require/use padding characters?</param>
		/// <param name="caseSensitive">Be case sensitive when decoding?</param>
		/// <param name="ignoreWhiteSpaceWhenDecoding">Ignore / allow white space when decoding?</param>
		public Base32Url(bool padding, bool caseSensitive, bool ignoreWhiteSpaceWhenDecoding) : this(padding, caseSensitive, ignoreWhiteSpaceWhenDecoding, Base32StandardAlphabet) { }
		/// <summary>
		/// Create case insensitive encoder/decoder with alternative alphabet and no padding.
		/// White space is not permitted when decoding (not ignored).
		/// </summary>
		/// <param name="alternateAlphabet">Alphabet to use (such as Base32Url.ZBase32Alphabet)</param>
		public Base32Url(string alternateAlphabet) : this(false, false, false, alternateAlphabet) { }
		/// <summary>
		/// Create the encoder/decoder specifying all options manually.
		/// </summary>
		/// <param name="padding">Require/use padding characters?</param>
		/// <param name="caseSensitive">Be case sensitive when decoding?</param>
		/// <param name="ignoreWhiteSpaceWhenDecoding">Ignore / allow white space when decoding?</param>
		/// <param name="alternateAlphabet">Alphabet to use (such as Base32Url.ZBase32Alphabet, Base32Url.Base32StandardAlphabet or your own custom 32 character alphabet string)</param>
		public Base32Url(bool padding, bool caseSensitive, bool ignoreWhiteSpaceWhenDecoding, string alternateAlphabet)
		{
			if (alternateAlphabet.Length != 32)
			{
				throw new ArgumentException("Alphabet must be exactly 32 characters long for base 32 encoding.");
			}
			PaddingChar = StandardPaddingChar;
			UsePadding = padding;
			IsCaseSensitive = caseSensitive;
			IgnoreWhiteSpaceWhenDecoding = ignoreWhiteSpaceWhenDecoding;
			_alphabet = alternateAlphabet;
		}
		/// <summary>
		/// Decode a base32 string to a byte[] using the default options
		/// (case insensitive without padding using the standard base32 alphabet from rfc4648).
		/// White space is not permitted (not ignored).
		/// Use alternative constructors for more options.
		/// </summary>
		public static byte[] FromBase32String(string input)
		{
			return new Base32Url().Decode(input);
		}
		/// <summary>
		/// Encode a base32 string from a byte[] using the default options
		/// (case insensitive without padding using the standard base32 alphabet from rfc4648).
		/// Use alternative constructors for more options.
		/// </summary>
		public static string ToBase32String(byte[] data)
		{
			return new Base32Url().Encode(data);
		}
		public string Encode(byte[] data)
		{
			StringBuilder result = new StringBuilder(Math.Max((int)Math.Ceiling(data.Length * 8 / 5.0), 1));
			byte[] emptyBuff = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
			byte[] buff = new byte[8];
			// take input five bytes at a time to chunk it up for encoding
			for (int i = 0; i < data.Length; i += 5)
			{
				int bytes = Math.Min(data.Length - i, 5);
				// parse five bytes at a time using an 8 byte ulong
				Array.Copy(emptyBuff, buff, emptyBuff.Length);
				Array.Copy(data, i, buff, buff.Length - (bytes+1), bytes);
				Array.Reverse(buff);
				ulong val = BitConverter.ToUInt64(buff, 0);
				for (int bitOffset = ((bytes+1) * 8) - 5; bitOffset > 3; bitOffset -= 5)
				{
					result.Append(_alphabet[(int)((val >> bitOffset) & 0x1f)]);
				}
			}
			if (UsePadding)
			{
				result.Append(string.Empty.PadRight((result.Length % 8) == 0 ? 0 : (8 - (result.Length % 8)), PaddingChar));
			}
			return result.ToString();
		}
		public byte[] Decode(string input)
		{
			if (IgnoreWhiteSpaceWhenDecoding)
			{
				input = Regex.Replace(input, "\\s+", "");
			}
			if (UsePadding)
			{
				if (input.Length % 8 != 0)
				{
					throw new ArgumentException("Invalid length for a base32 string with padding.");
				}
				input = input.TrimEnd(PaddingChar);
			}
			// index the alphabet for decoding only when needed
			EnsureAlphabetIndexed();
			MemoryStream ms = new MemoryStream(Math.Max((int)Math.Ceiling(input.Length * 5 / 8.0), 1));
			// take input eight bytes at a time to chunk it up for encoding
			for (int i = 0; i < input.Length; i += 8)
			{
				int chars = Math.Min(input.Length - i, 8);
				ulong val = 0;
				int bytes = (int)Math.Floor(chars * (5 / 8.0));
				for (int charOffset = 0; charOffset < chars; charOffset++)
				{
					uint cbyte;
					if (!_index.TryGetValue(input.Substring(i + charOffset, 1), out cbyte))
					{
						throw new ArgumentException("Invalid character '" + input.Substring(i + charOffset, 1) + "' in base32 string, valid characters are: " + _alphabet);
					}
					val |= (((ulong)cbyte) << ((((bytes + 1) * 8) - (charOffset * 5)) - 5));
				}
				byte[] buff = BitConverter.GetBytes(val);
				Array.Reverse(buff);
				ms.Write(buff, buff.Length - (bytes + 1), bytes);
			}
			return ms.ToArray();
		}
		private void EnsureAlphabetIndexed()
		{
			if (_index == null)
			{
				Dictionary<string, uint> cidx;
				string indexKey = (IsCaseSensitive ? "S" : "I") + _alphabet;
				if (!_indexes.TryGetValue(indexKey, out cidx))
				{
					lock (_indexes)
					{
						if (!_indexes.TryGetValue(indexKey, out cidx))
						{
							cidx = new Dictionary<string, uint>(_alphabet.Length, IsCaseSensitive ? StringComparer.InvariantCulture : StringComparer.InvariantCultureIgnoreCase);
							for (int i = 0; i < _alphabet.Length; i++)
							{
								cidx[_alphabet.Substring(i, 1)] = (uint) i;
							}
							_indexes.Add(indexKey, cidx);
						}
					}
				}
				_index = cidx;
			}
		}
	}
}
 

Base64Url

using System;
using System.Text;
namespace MhanoHarkness
{
	/// <summary>
	/// Modified Base64 for URL applications ('base64url' encoding)
	/// 
	/// See http://tools.ietf.org/html/rfc4648
	/// For more information see http://en.wikipedia.org/wiki/Base64
	/// </summary>
	public class Base64Url
	{
		/// <summary>
		/// Modified Base64 for URL applications ('base64url' encoding)
		/// 
		/// See http://tools.ietf.org/html/rfc4648
		/// For more information see http://en.wikipedia.org/wiki/Base64
		/// </summary>
		/// <param name="input"></param>
		/// <returns>Input byte array converted to a base64ForUrl encoded string</returns>
		public static string ToBase64ForUrlString(byte[] input)
		{
			StringBuilder result = new StringBuilder(Convert.ToBase64String(input).TrimEnd('='));
			result.Replace('+', '-');
			result.Replace('/', '_');
			return result.ToString();
		}
		/// <summary>
		/// Modified Base64 for URL applications ('base64url' encoding)
		/// 
		/// See http://tools.ietf.org/html/rfc4648
		/// For more information see http://en.wikipedia.org/wiki/Base64
		/// </summary>
		/// <param name="base64ForUrlInput"></param>
		/// <returns>Input base64ForUrl encoded string as the original byte array</returns>
		public static byte[] FromBase64ForUrlString(string base64ForUrlInput)
		{
			int padChars = (base64ForUrlInput.Length%4) == 0 ? 0 : (4 - (base64ForUrlInput.Length%4));
			StringBuilder result = new StringBuilder(base64ForUrlInput, base64ForUrlInput.Length + padChars);
			result.Append(String.Empty.PadRight(padChars, '='));
			result.Replace('-', '+');
			result.Replace('_', '/');
			return Convert.FromBase64String(result.ToString());
		}
	}
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

mhano
Architect
Australia Australia
No Biography provided

Comments and Discussions

 
GeneralAwesome., PinmemberMrChrisBarker1-Aug-14 0:48 
GeneralMy vote of 5 Pinmembergrapevine6-Sep-13 10:06 
GeneralRe: My vote of 5 Pinmembermhano2-May-14 1:30 
GeneralMy vote of 5 Pinmembertomexou26-Feb-13 4:13 
Generalhow to decode base32 encoded text? Sorry, i'm still didn't ... Pinmemberrizkyaqew25-Aug-11 3:52 
GeneralReason for my vote of 5 Works like a charm, saved my time du... PinmemberIvan Vasilyev4-Aug-10 18:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 2 May 2010
Article Copyright 2010 by mhano
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid