Skip to Content
Course content

133: encoding/base64 and encoding/hex

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're trying to send a detailed drawing to a friend, but the only way you can communicate is through a very old-fashioned telegraph that only accepts the 26 letters of the alphabet and a few numbers. You can't send a "pixel" or a "line" directly; you have to describe the drawing using a specific code—maybe a series of letters that represent coordinates—which your friend then decodes back into a drawing on their end. The drawing hasn't changed, but the representation of it has shifted so it can survive the trip through a limited system.

In Go, this is exactly what encoding/base64 and encoding/hex do. We often have binary data (like an image, a hashed password, or a cryptographic key) represented as a []byte. The problem is that many systems—like JSON APIs, HTTP headers, or old database columns—don't play nice with raw binary. They expect text. So, we "translate" those bytes into a safe, text-based format and back again.

Turning bytes into something human-readable with hex

Hexadecimal (base16) is my go-to when I'm debugging. Since it only uses 0-9 and a-f, it's very compact and easy to spot in a log file. If you've ever seen a MAC address or a color code like #FF5733, you've seen hex.

package main

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

func main() {
	// Imagine this is a raw cryptographic salt or a small binary blob
	rawBytes := []byte("Go is awesome!")

	// Encode: binary -> text
	encoded := hex.EncodeToString(rawBytes)
	fmt.Printf("Hex encoded: %s\n", encoded) 
	// Output: 476f20697320617765736f6d6521

	// Decode: text -> binary
	decoded, err := hex.DecodeString(encoded)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Decoded: %s\n", string(decoded))
}

I usually use hex when the data needs to be readable by a human during a trace, but it's not the most efficient way to move large amounts of data because it doubles the size of the original input.

Packing data for the web with base64

Base64 is the industry standard for embedding binary data into text formats. It uses a larger alphabet (A-Z, a-z, 0-9, +, and /), which makes it more efficient than hex. If you've ever seen a "Data URI" for an image in HTML, that's base64.

One thing that tripped me up when I first started with base64 in Go is that there isn't just one "encoder." Depending on where the data is going, you need a different alphabet. For example, the + and / characters in standard base64 have special meanings in URLs. If you put them in a URL, the browser might mangle them.

package main

import (
	"encoding/base64"
	"fmt"
)

func main() {
	data := []byte("User: admin; Pass: 12345")

	// Standard encoding - great for JSON or Email
	stdEncoded := base64.StdEncoding.EncodeToString(data)
	fmt.Println("Standard:", stdEncoded)

	// URL encoding - swaps + and / for - and _ to be URL-safe
	urlEncoded := base64.URLEncoding.EncodeToString(data)
	fmt.Println("URL Safe:", urlEncoded)

	// Decoding back
	decodedBytes, _ := base64.StdEncoding.DecodeString(stdEncoded)
	fmt.Printf("Original: %s\n", string(decodedBytes))
}

Choosing the right tool for the job

You might be wondering, "Why not just use one for everything?" It comes down to the environment. I follow a simple rule of thumb: use hex for logs and low-level debugging where clarity is key. Use base64.StdEncoding for internal data transfer (like inside a JSON body). Use base64.URLEncoding the second that data touches a URL parameter or a cookie. If you mix them up, you'll end up with "illegal character" errors that are a pain to track down.




📋 Practical Task

Exercise: Build a Binary Secret-Key Transformer

You are building a small utility for a security team. They need to be able to take a raw binary "secret key" (represented as a byte slice), convert it into a URL-safe string to be sent in a GET request, and then convert it back to bytes on the receiving server.

Your requirements:

  • Create a function EncodeKey(key []byte) string that uses URL-safe base64 encoding.
  • Create a function DecodeKey(encodedKey string) ([]byte, error) that reverts the string back to the original byte slice.
  • In your main function, define a byte slice []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}.
  • Pass this slice through your encoder, print the resulting string, then pass that string through your decoder and verify the bytes match the original.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.