Base32 Decoding
Solution 1
I had a need for a base32 encoder/decoder, so I spent a couple hours this afternoon throwing this together. I believe it conforms to the standards listed here: https://www.rfc-editor.org/rfc/rfc4648#section-6.
public class Base32Encoding
{
public static byte[] ToBytes(string input)
{
if (string.IsNullOrEmpty(input))
{
throw new ArgumentNullException("input");
}
input = input.TrimEnd('='); //remove padding characters
int byteCount = input.Length * 5 / 8; //this must be TRUNCATED
byte[] returnArray = new byte[byteCount];
byte curByte = 0, bitsRemaining = 8;
int mask = 0, arrayIndex = 0;
foreach (char c in input)
{
int cValue = CharToValue(c);
if (bitsRemaining > 5)
{
mask = cValue << (bitsRemaining - 5);
curByte = (byte)(curByte | mask);
bitsRemaining -= 5;
}
else
{
mask = cValue >> (5 - bitsRemaining);
curByte = (byte)(curByte | mask);
returnArray[arrayIndex++] = curByte;
curByte = (byte)(cValue << (3 + bitsRemaining));
bitsRemaining += 3;
}
}
//if we didn't end with a full byte
if (arrayIndex != byteCount)
{
returnArray[arrayIndex] = curByte;
}
return returnArray;
}
public static string ToString(byte[] input)
{
if (input == null || input.Length == 0)
{
throw new ArgumentNullException("input");
}
int charCount = (int)Math.Ceiling(input.Length / 5d) * 8;
char[] returnArray = new char[charCount];
byte nextChar = 0, bitsRemaining = 5;
int arrayIndex = 0;
foreach (byte b in input)
{
nextChar = (byte)(nextChar | (b >> (8 - bitsRemaining)));
returnArray[arrayIndex++] = ValueToChar(nextChar);
if (bitsRemaining < 4)
{
nextChar = (byte)((b >> (3 - bitsRemaining)) & 31);
returnArray[arrayIndex++] = ValueToChar(nextChar);
bitsRemaining += 5;
}
bitsRemaining -= 3;
nextChar = (byte)((b << bitsRemaining) & 31);
}
//if we didn't end with a full char
if (arrayIndex != charCount)
{
returnArray[arrayIndex++] = ValueToChar(nextChar);
while (arrayIndex != charCount) returnArray[arrayIndex++] = '='; //padding
}
return new string(returnArray);
}
private static int CharToValue(char c)
{
int value = (int)c;
//65-90 == uppercase letters
if (value < 91 && value > 64)
{
return value - 65;
}
//50-55 == numbers 2-7
if (value < 56 && value > 49)
{
return value - 24;
}
//97-122 == lowercase letters
if (value < 123 && value > 96)
{
return value - 97;
}
throw new ArgumentException("Character is not a Base32 character.", "c");
}
private static char ValueToChar(byte b)
{
if (b < 26)
{
return (char)(b + 65);
}
if (b < 32)
{
return (char)(b + 24);
}
throw new ArgumentException("Byte is not a value Base32 value.", "b");
}
}
Solution 2
Check this FromBase32String
implementation for .NET found here.
Edit: The above link was dead; you can find an archived copy at archive.org
The actual code read:
using System;
using System.Text;
public sealed class Base32 {
// the valid chars for the encoding
private static string ValidChars = "QAZ2WSX3" + "EDC4RFV5" + "TGB6YHN7" + "UJM8K9LP";
/// <summary>
/// Converts an array of bytes to a Base32-k string.
/// </summary>
public static string ToBase32String(byte[] bytes) {
StringBuilder sb = new StringBuilder(); // holds the base32 chars
byte index;
int hi = 5;
int currentByte = 0;
while (currentByte < bytes.Length) {
// do we need to use the next byte?
if (hi > 8) {
// get the last piece from the current byte, shift it to the right
// and increment the byte counter
index = (byte)(bytes[currentByte++] >> (hi - 5));
if (currentByte != bytes.Length) {
// if we are not at the end, get the first piece from
// the next byte, clear it and shift it to the left
index = (byte)(((byte)(bytes[currentByte] << (16 - hi)) >> 3) | index);
}
hi -= 3;
} else if(hi == 8) {
index = (byte)(bytes[currentByte++] >> 3);
hi -= 3;
} else {
// simply get the stuff from the current byte
index = (byte)((byte)(bytes[currentByte] << (8 - hi)) >> 3);
hi += 5;
}
sb.Append(ValidChars[index]);
}
return sb.ToString();
}
/// <summary>
/// Converts a Base32-k string into an array of bytes.
/// </summary>
/// <exception cref="System.ArgumentException">
/// Input string <paramref name="s">s</paramref> contains invalid Base32-k characters.
/// </exception>
public static byte[] FromBase32String(string str) {
int numBytes = str.Length * 5 / 8;
byte[] bytes = new Byte[numBytes];
// all UPPERCASE chars
str = str.ToUpper();
int bit_buffer;
int currentCharIndex;
int bits_in_buffer;
if (str.Length < 3) {
bytes[0] = (byte)(ValidChars.IndexOf(str[0]) | ValidChars.IndexOf(str[1]) << 5);
return bytes;
}
bit_buffer = (ValidChars.IndexOf(str[0]) | ValidChars.IndexOf(str[1]) << 5);
bits_in_buffer = 10;
currentCharIndex = 2;
for (int i = 0; i < bytes.Length; i++) {
bytes[i] = (byte)bit_buffer;
bit_buffer >>= 8;
bits_in_buffer -= 8;
while (bits_in_buffer < 8 && currentCharIndex < str.Length) {
bit_buffer |= ValidChars.IndexOf(str[currentCharIndex++]) << bits_in_buffer;
bits_in_buffer += 5;
}
}
return bytes;
}
}
Solution 3
This is a really old question, but I happened to stumble on it wanting the same thing for OTP tokens. It turns out that there is base 32 functionality built into the OTP.NET package on NuGet:
Base32Encoding.ToBytes("(your base 32 string here)")
The reverse is also possible:
Base32Encoding.ToString(new byte[] { /* your bytes here */ })
Solution 4
Here are my functions for encoding and decoding. I feel that they are much shorter and concise than the other suggestions. So if you need a small one, try these.
public static string BytesToBase32(byte[] bytes) {
const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
string output = "";
for (int bitIndex = 0; bitIndex < bytes.Length * 8; bitIndex += 5) {
int dualbyte = bytes[bitIndex / 8] << 8;
if (bitIndex / 8 + 1 < bytes.Length)
dualbyte |= bytes[bitIndex / 8 + 1];
dualbyte = 0x1f & (dualbyte >> (16 - bitIndex % 8 - 5));
output += alphabet[dualbyte];
}
return output;
}
public static byte[] Base32ToBytes(string base32) {
const string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
List<byte> output = new List<byte>();
char[] bytes = base32.ToCharArray();
for (int bitIndex = 0; bitIndex < base32.Length * 5; bitIndex += 8) {
int dualbyte = alphabet.IndexOf(bytes[bitIndex / 5]) << 10;
if (bitIndex / 5 + 1 < bytes.Length)
dualbyte |= alphabet.IndexOf(bytes[bitIndex / 5 + 1]) << 5;
if (bitIndex / 5 + 2 < bytes.Length)
dualbyte |= alphabet.IndexOf(bytes[bitIndex / 5 + 2]);
dualbyte = 0xff & (dualbyte >> (15 - bitIndex % 5 - 8));
output.Add((byte)(dualbyte));
}
return output.ToArray();
}
Solution 5
I've written some flexible standards based implementations of various Base32 and Base64 encoding/decoding methods. Notably: base64url (per rfc4648) and its base32 equivalent.
By default the Base32Url class 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, case sensitivity/insensitivity, padding/no-padding etc.
trampster
I am the creator of JsonSrcGen https://github.com/trampster/JsonSrcGen a high performance .net Json Library that uses .net 5 Source Generators
Updated on July 09, 2022Comments
-
trampster almost 2 years
I have a base32 string which I need to convert to a byte array. And I'm having trouble finding a conversion method in the .NET framework. I can find methods for base64 but not for base32.
Convert.FromBase64String
– something like this for base32 would be perfect.Is there such a method in the framework or do I have to roll my own?
-
Jamie almost 3 yearsI know this is from forever ago, but you should consider changing the accepted answer. If someone blindly just looks at the accepted answer without digging further (their fault, I know), then they would be setting themselves up for some hurt.
-
-
Daniel LeCheminant over 15 yearsCharToValue can be made much simpler using casting and subtraction ;-] For example, 'b' - 'a' = 1
-
leppie over 15 yearsCharToValue is a WTF! Also whats the use of a decoder without an encoder?
-
Nick over 12 yearsDoes not give me the expected results
-
Devin almost 12 years+1 Awesome code. I have been looking for a good base 32 encoder/decoder. I set up a bunch of unit tests from the RFC test vectors and nothing here (that I had tried) passed them all. Yours did first shot. Nice job. I am surprised there aren't more upvotes.
-
Darkthread over 11 yearsMy suggestion: "charCount = (int)Math.Ceiling(input.Length / 5d) * 8;" should be "(int)Math.Ceiling(input.Length / 5d * 8)" to remove redundant "=" padding.
-
Albireo over 10 yearsHello, I used your code in an application I'm building, may I re-license it under the MIT license (with due credit)? You can find the code at github.com/kappa7194/otp/blob/master/Albireo.Otp.Library/…
-
Dirk Bester about 10 yearsHello, I am using your code and would like to license it under opengl-tutorial.org/download
-
Ka0s over 9 yearsThank you, this is the first working answer I find after searching too long. Great code
-
spacer about 7 yearsAs said on the original page, this is not standard Base32 encoding but it is nice if you need "obfuscated" Base32. +1
-
spacer about 7 yearsWell,
FromBase32String()
is not throwingArgumentException
(or any other) for invalid characters - becauseIndexOf
doesn't either.IndexOf
is returning -1 if character is not found. -
spacer about 7 yearsA solution would be to define an extension method for
string
, e.g. "IndexOfEx", which would throw the exception instead of returning -1. -
sidon over 6 yearsI like how easy to read this alg is. However decoding appends zero byte at the end if number of source bytes is not multiply of 5
-
ogggre over 4 yearsBEWARE of this implementation. It does not comply to any standards of interoperability, and literally poisoned the whole .NET ecosystem. It goes kaboom on some input strings. What you really need is something complying to RFC 4648.
-
Taylor Buchanan over 4 yearsThe source of Otp.NET points to Shane's answer on this question...
-
Joe Skeen over 4 yearsGood to know @TaylorBuchanan! I think it's still nice to know that there's a library for it now, so no need to go reinventing it ;)
-
Jamie almost 3 yearsHopefully, people will look at the second solution instead of blindly going with this one.
-
Patrick over 2 yearschange the
for
contition tobitIndex / 5 + 1 < bytes.Length