Like the more common Base64 encoding, Base32 encoding is a method for turning binary data into a string composed of a small, defined set of ASCII characters. Base64 takes advantage of the entire alphabet in both upper and lower case, the digits 0 to 9 and the '+' and '/' symbols. This can be problematic where, for example, the encoded data needs to be used as part of a URL where the '+' and '/' symbols have special meaning and where case-sensitivity can cause problems.
Base32 addresses these issues by using a further reduced set of characters - the entire alphabet (but only one case) and the digits 2 to 7. The digits '0' and '1' are ommitted due to their similarity to the letters 'O' and 'I'. This makes Base32 even more useful in situations where human readability is a concern.
More information about Base32 can, of course, be found on the Base32 Wikipedia page.
During a recent project, I had a requirement to use Base32 encoding and was a little surprised to find that the .NET library doesn't include support for it alongside Base64 in the System.Convert namespace. So, I went about writing my own support as a pair of extension methods.
For the sake of tidyness, I originally wanted to add these extension methods to the System.Convert class, but it seems it's impossible to create Shared extension methods in VB.NET at the moment. So - I have written one extension method for the String class which converts a Base32 encoded string into a Byte array. The other extension method is for the Byte array type which converts the binary data into a Base 32 encoded string.
The code is included below - please feel free to use it. If you have any comments or suggestions for improvements, please let me know. Enjoy!
Imports System.Runtime.CompilerServices
Module Base32
Const cBase32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
Const cBase32Pad = "="
<Extension()> _
Public Function ToBase32String(ByVal Data As Byte(), Optional IncludePadding As Boolean = True) As String
Dim RetVal As String = ""
Dim Segments As New List(Of Long)
' Divide the input data into 5 byte segments
Dim Index As Integer = 0
While Index < Data.Length
Dim CurrentSegment As Long = 0
Dim SegmentSize As Integer = 0
While Index < Data.Length And SegmentSize < 5
CurrentSegment <<= 8
CurrentSegment += Data(Index)
Index += 1
SegmentSize += 1
End While
' If the size of the last segment was less than 5 bytes, pad with zeros
CurrentSegment <<= (8 * (5 - SegmentSize))
Segments.Add(CurrentSegment)
End While
' Convert each 5 byte segment into 8 character strings
For Each CurrentSegment As Long In Segments
For x As Integer = 0 To 7
RetVal &= cBase32Alphabet.Chars((CurrentSegment >> (7 - x) * 5) And &H1F)
Next
Next
' Correct the end of the string (where the input wasn't a multiple of 5 bytes)
Dim LastSegmentUsefulDataLength As Integer = Math.Ceiling((Data.Length Mod 5) * 8 / 5)
RetVal = RetVal.Substring(0, RetVal.Length - (8 - LastSegmentUsefulDataLength))
' Add the padding characters
If IncludePadding Then
RetVal &= New String(cBase32Pad, 8 - LastSegmentUsefulDataLength)
End If
Return RetVal
End Function
<Extension()> _
Public Function FromBase32String(ByVal Data As String) As Byte()
Dim RetVal As New List(Of Byte)
Dim Segments As New List(Of Long)
' Remove any trailing padding
Data = Data.TrimEnd(New Char() {cBase32Pad})
' Process the string 8 characters at a time
Dim Index As Integer = 0
While Index < Data.Length
Dim CurrentSegment As Long = 0
Dim SegmentSize As Integer = 0
While Index < Data.Length And SegmentSize < 8
CurrentSegment <<= 5
CurrentSegment = CurrentSegment Or cBase32Alphabet.IndexOf(Data.Chars(Index))
Index += 1
SegmentSize += 1
End While
' If the size of the last segment was less than 40 bits, pad it
CurrentSegment <<= (5 * (8 - SegmentSize))
Segments.Add(CurrentSegment)
End While
' Break the 5 byte segments back down into individual bytes
For Each CurrentSegment As Long In Segments
For x = 0 To 4
RetVal.Add((CurrentSegment >> (4 - x) * 8) And &HFF)
Next
Next
' Remove any bytes of padding from the output
Dim BytesToRemove As Integer = 5 - (Math.Ceiling(Math.Ceiling(3 * 8 / 5) / 2))
RetVal.RemoveRange(RetVal.Count - BytesToRemove, BytesToRemove)
Return RetVal.ToArray()
End Function
End Module
This code can then be used to encode / decode data as follows:
Dim DataToEncode As Byte() = {54, 124, 84, 25, 19, 156}
Dim Base32 As String
Base32 = DataToEncode.ToBase32String()
Dim DecodedData As Byte()
DecodedData = Base32.FromBase32String()
Thanks for the code - exactly what I was looking for, and I also had the same idea (using the base 32 string for a URL). However, it turned out not to be the huge time saver I hoped for as I had to fix a couple of critical bugs. But I'll share the bug fixes here so everyone can benefit.
Fix #1 for ToBase32String():
Insert after the "Dim LastSegmentUsefulDataLength" line:
If LastSegmentUsefulDataLength = 0 Then
LastSegmentUsefulDataLength = 8
End If
Fix #2 for FromBase32String():
Replace the "Dim BytesToRemove" line with these 2 lines:
Dim BytesToKeep As Integer = CInt(Math.Floor(CDbl(Data.Length) * 5 / 8))
Dim BytesToRemove As Integer = RetVal.Count - BytesToKeep
Handy verification routine:
Public Sub TestBase32()
Dim i As Integer
Dim j As Integer
Dim Bytes, Output As Byte()
For i = 104 To 0 Step -1
Bytes = Array.CreateInstance(GetType(Byte), i)
For j = 0 To i - 1
Bytes(j) = 255 - i + j
Next j
Output = FromBase32String(ToBase32String(Bytes, True))
If Bytes.Length <> Output.Length Then
Throw New Exception("Old Length = " & Bytes.Length & ", New Length = " & Output.Length)
End If
For j = 0 To i - 1
If Bytes(j) <> Output(j) Then
Throw New Exception("Old Byte(" & j & ") = " & Bytes(j).ToString() & ", New Length = " & Output(j).ToString())
End If
Next j
Next i
End Sub
I'm Chris Roberts, a software developer turned business owner. Join me as I grow a great software business in Cheltenham, England ...

Receive updates automatically using your RSS reader.