Welcome To Golang By Example

Base64 encoding/Decoding in Go (Golang)

Overview

Golang provides an encoding/base64 package that can be used to encode strings to base64 and decode the base64 encoded string back to the original string. https://golang.org/pkg/encoding/base64/

Go supports base64 in 4 different ways.

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_

Each of the above encodings is represented by the Encoding struct

https://golang.org/pkg/encoding/base64/#Encoding

Encoding struct further defines two methods for encoding and decoding

func (enc *Encoding) EncodeToString(src []byte) string
func (enc *Encoding) DecodeString(s string) ([]byte, error)

Let’s see a detailed example of each

StdEncoding

This is the standard base64 encoding which is defined in RFC 4648. It uses the below 64 characters set along with padding character ‘=’.

Value Encoding  Value Encoding  Value Encoding  Value Encoding
         0 A            17 R            34 i            51 z
         1 B            18 S            35 j            52 0
         2 C            19 T            36 k            53 1
         3 D            20 U            37 l            54 2
         4 E            21 V            38 m            55 3
         5 F            22 W            39 n            56 4
         6 G            23 X            40 o            57 5
         7 H            24 Y            41 p            58 6
         8 I            25 Z            42 q            59 7
         9 J            26 a            43 r            60 8
        10 K            27 b            44 s            61 9
        11 L            28 c            45 t            62 +
        12 M            29 d            46 u            63 /
        13 N            30 e            47 v
        14 O            31 f            48 w         (pad) =
        15 P            32 g            49 x
        16 Q            33 h            50 y

Let’s see an example of this

Example

package main

import (
	"encoding/base64"
	"fmt"
	"log"
)

func main() {
	sample := "a@"
	encodedString := base64.StdEncoding.EncodeToString([]byte(sample))
	fmt.Println(encodedString)

	originalStringBytes, err := base64.StdEncoding.DecodeString(encodedString)
	if err != nil {
		log.Fatalf("Some error occured during base64 decode. Error %s", err.Error())
	}
	fmt.Println(string(originalStringBytes))
}

Output

YUA=
a@

Notice that in the above example we are encoding the below string using base64 Std encoding

sample := "a@"
encodedString := base64.StdEncoding.EncodeToString([]byte(sample))

Then we are decoding the base64 encoded string back to the original string

originalStringBytes, err := base64.StdEncoding.DecodeString(encodedString)
if err != nil {
	log.Fatalf("Some error occured during base64 decode. Error %s", err.Error())
}
fmt.Println(string(originalStringBytes))

It correctly outputs the original string back on decoding

URLEncoding

In this, the ‘+’ and ‘\’ sign is replaced with ‘-‘ and ‘_’. They are replaced to make them compatible with filenames and URLs. 

URL: In URLs ‘+’ and ‘\’  are further encoded into hexadecimal sequences due to URL encoding thereby further increasing the length of the URL. For eg ‘+’ will be converted to ‘%2B’ and ‘\’ will be encoded to ‘%2F’ in the URL encoding.

Filenames‘\’ is used in file paths in both Unix and Windows. Hence it ‘\’ is replaced with ‘_’

Let’s see an example

package main

import (
	"encoding/base64"
	"fmt"
	"log"
)

func main() {
	sample := "�"

	encodedStringURL := base64.URLEncoding.EncodeToString([]byte(sample))
	fmt.Printf("URL Encoding: %s\n", encodedStringURL)
	encodedStringSTD := base64.StdEncoding.EncodeToString([]byte(sample))
	fmt.Printf("STD Encoding: %s\n", encodedStringSTD)

	originalStringBytes, err := base64.URLEncoding.DecodeString(encodedStringURL)
	if err != nil {
		log.Fatalf("Some error occured during base64 decode. Error %s", err.Error())
	}
	fmt.Println(string(originalStringBytes))
}

Output

URL Encoding: 77-9
STD Encoding: 77+9
�

Notice that in the above example we have taken the example of the below string

sample := "�"

Then we are printing both the StdEncoding as well as URL Encoding of the above string

encodedStringURL := base64.URLEncoding.EncodeToString([]byte(sample))
fmt.Printf("URL Encoding: %s\n", encodedStringURL)
encodedStringSTD := base64.StdEncoding.EncodeToString([]byte(sample))
fmt.Printf("STD Encoding: %s\n", encodedStringSTD)

Notice the difference in output

For URL encoding it prints

77-9

while for STD encoding, it prints

77+9

This is because ‘+’ is present in STD encoding but in URL encoding ‘+’ is replaced with ‘-‘

RawStdEncoding

This is the same as StdEncoding just that it omits padding characters.  So it is unpadded STD base64 encoding. Let’s see an example. Before we see an example of Raw STD encoding we want to explain why the raw version of std encoding exists which omits padding characters.  That brings a very important topic related to base64 encoding which is ‘Is padding necessary’. The answer is that it depends

Actual StringBase64 Encoded with PaddingBase64 Encoded without Padding
aYQ==YQ
bcYmM=YmM
defZGVmZGVm

Now let’s consider both cases.

When the concatenation is sent without padding

In this case, the concatenated Base64 string will be

YQYmMZGVm

Try decoding it and you will get the final string as below which is incorrect

a&1

When the concatenation is sent with padding

In this case, the concatenated Base64 string will be

YQ==YmM=ZGVm

Try decoding it in groups of 4 characters and you will get the final string as below which is correct

abcdef

Now again the question that comes to mind is why do you need to concatenate multiple base64 encoded strings. The answer is that it is always good in cases where there is streaming data and you want to send the base64 encoded data as it comes. For example, buffering of video.

So that is why padding is encouraged though not absolutely not necessary in all cases. Now let’s see an example of Raw Std Encoding

package main

import (
	"encoding/base64"
	"fmt"
	"log"
)

func main() {
	sample := "a@"
	encodedStringStdEncoding := base64.StdEncoding.EncodeToString([]byte(sample))
	fmt.Printf("STD Encoding: %s\n", encodedStringStdEncoding)

	encodedStringRawStdEncoding := base64.RawStdEncoding.EncodeToString([]byte(sample))
	fmt.Printf("Raw STD Encoding: %s\n", encodedStringRawStdEncoding)

	originalStringBytes, err := base64.RawStdEncoding.DecodeString(encodedStringRawStdEncoding)
	if err != nil {
		log.Fatalf("Some error occured during base64 decode. Error %s", err.Error())
	}
	fmt.Println(string(originalStringBytes))
}

Output

STD Encoding: YUA=
Raw STD Encoding: YUA
a@

In the above example we are printing both the result of both StdEncoding as well as RawStdEncoding

encodedStringStdEncoding := base64.StdEncoding.EncodeToString([]byte(sample))
fmt.Printf("STD Encoding: %s\n", encodedStringStdEncoding)

encodedStringRawStdEncoding := base64.RawStdEncoding.EncodeToString([]byte(sample))
fmt.Printf("Raw STD Encoding: %s\n", encodedStringRawStdEncoding)

Notice the difference in output

For STD encoding it prints

YUA=

while for Raw STD encoding, it prints

YUA

As you can notice that RAW STD Encoding omits padding characters.

RawURLEncoding

This is also the same as URL encoding just that it omits padding characters. So it is unpadded STD base64 encoding.

package main
import (
    "encoding/base64"
    "fmt"
    "log"
)
func main() {
    sample := "a@"
    encodedStringURLEncoding := base64.URLEncoding.EncodeToString([]byte(sample))
    fmt.Printf("URL Encoding: %s\n", encodedStringURLEncoding)

    encodedStringRawURLEncoding := base64.RawURLEncoding.EncodeToString([]byte(sample))
    fmt.Printf("Raw URL Encoding: %s\n", encodedStringRawURLEncoding)

    originalStringBytes, err := base64.RawStdEncoding.DecodeString(encodedStringRawURLEncoding)
    if err != nil {
        log.Fatalf("Some error occured during base64 decode. Error %s", err.Error())
    }
    fmt.Println(string(originalStringBytes))
}

 Output

URL Encoding: YUA=
Raw URL Encoding: YUA
a@

In the above example, we are again printing the result of both URL encoding and as well as RAW URL encoding and you can notice that padding character ‘=’ is omitted in the Raw URL encoding.