目录

记录一个 Go base64 解码的问题

这里记录一个使用 Go base64 标准库解码 token 时遇到的问题。

import (
	"encoding/base64"
	"fmt"
)

func tokenParser(token string) {
	tokenStr, err := base64.StdEncoding.DecodeString(token)
	if err != nil {
		fmt.Println(err)
	}
}

上面的代码输出了错误 illegal base64 data at input byte xxx

上面的错误是因为 jwt 的 base64 是 no padding 的,要使用 base64.RawStdEncoding 来解码:

tokenStr, err := base64.RawStdEncoding.DecodeString(token)
if err != nil {
    fmt.Println(err)
}

什么是 no padding

要知道什么是 no padding,需要先简单了解一下 base64 的原理。

base64 编码原理

base64 是网络上最常见的用于传输 8 bit 字节码的编码方式之一,base64 就是一种基于 64 个可打印字符来表示二进制数据的方法。

64 个打印字符:

索引对应字符索引对应字符索引对应字符索引对应字符
0A17R34i51z
1B18S35j520
2C19T36k531
3D20U37l542
4E21V38m553
5F22W39n564
6G23X40o575
7H24Y41p586
8I25Z42q597
9J26a43r608
10K27b44s619
11L28c45t62+
12M29d46u63/
13N30e47v
14O31f48w
15P32g49x
16Q33h50y

base64 就是使用上面的 64 个可打印字符来表示二进制数据。2^6 = 64 也就是说,上面 64 个字符的索引,最多用 6 个 bit 就可以表示了。 但是常用的字符集没有使用 6 bit 表示的,比如 ASCII 码需要 8 个 bit 来表示。

那么如何使用 6 个 bit 表示 8 个 bit 的数据?

使用 4*6 个 bit 来存储 3*8 个 bit。例如:

/images/go-base64/go-base64-sample1.png

Son 经过 base64 编码以后转换成了 U29u

3 个 ASCII 字符刚好转换成对应的 4 个 base64 字符,但是如果需要转换的字符不是 3 的倍数,也就是说在分组时最后一组不够 3 个字节如何转换?

base64 有一条规则:当需要转换的字符不是 3 的倍数时,一律采用补 0 的方式凑足 3 的倍数。例如:

/images/go-base64/go-base64-sample2.png

S 经过 base64 编码以后转换成了 Uw==。第二组末尾补 4 个 0 转换后为字符 w。剩下两个字节使用 = 填补。

no padding 是非填补的意思,也就是说当需要转换的字符不是 3 的倍数时,剩下的 1 到 2 个 0 字节不使用 = 填补

Go base64 标准库

Go base64 标准库的源码:

// StdEncoding is the standard base64 encoding, as defined in
// RFC 4648.
var StdEncoding = NewEncoding(encodeStd)

// URLEncoding is the alternate base64 encoding defined in RFC 4648.
// It is typically used in URLs and file names.
var URLEncoding = NewEncoding(encodeURL)

// RawStdEncoding is the standard raw, unpadded base64 encoding,
// as defined in RFC 4648 section 3.2.
// This is the same as StdEncoding but omits padding characters.
var RawStdEncoding = StdEncoding.WithPadding(NoPadding)

// RawURLEncoding is the unpadded alternate base64 encoding defined in RFC 4648.
// It is typically used in URLs and file names.
// This is the same as URLEncoding but omits padding characters.
var RawURLEncoding = URLEncoding.WithPadding(NoPadding)

StdEncoding 代表的是标准加解密,URLEncoding,则是 URL 加解密。RawStdEncoding 和 RawURLEncoding 非别对应它们在 no padding 时应该 使用的方法。