记录一个 Go base64 解码的问题

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

1
2
3
4
5
6
7
8
9
10
11
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 来解码:

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

什么是 no padding?

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

base64 编码原理

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

64 个打印字符:

索引 对应字符 索引 对应字符 索引 对应字符 索引 对应字符
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
15 P 32 g 49 x
16 Q 33 h 50 y

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

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

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

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

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

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

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

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

Go base64 标准库

Go base64 标准库的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 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 时应该
使用的方法。