一. 概念简介
Base64 是一种基于 64 个可打印字符来表示二进制数据的表示方法. 由于 2^6=64, 所以每 6 个比特为一个单元, 对应某个可打印字符. 3 个字节有 24 个比特, 对应于 4 个 Base64 单元, 即 3 个字节可由 4 个可打印字符来表示. 它可用来作为电子邮件的传输编码. 在 Base64 中的可打印字符包括字母 A-Z,a-z, 数字 0-9, 这样共有 62 个字符, 此外两个可打印符号在不同的系统中而不同.
Base64 常用于在通常处理文本数据的场合, 表示, 传输, 存储一些二进制数据
二. 代码调用
在 Golang 中提供了代码库可以供我们直接调用, 用于实现 Base64 的编码与解码, 其提供了对两种格式的数据进行编码 (与解码)
- const encodeStd = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
- const encodeURL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
来自 golang 源码 base64.go
1. 标准数据 (encodeStd)
- msg := "Mac"
- // 编码
- base64EncodedString := base64.StdEncoding.EncodeToString([]byte(msg))
- fmt.Println("Base64 编码后:", base64EncodedString)
- // 解码
- base64DecodedByte, err := base64.StdEncoding.DecodeString(base64EncodedString)
- if err != nil {
- log.Panic(err)
- }
- fmt.Println("Base64 解码后的字节数组:", base64DecodedByte)
- fmt.Println("Base64 解码后:", string(base64DecodedByte))
返回打印结果
Base64 编码后: TWFj
Base64 解码后的字节数组: [77 97 99]
Base64 解码后: Mac
2.URL 数据 (encodeURL)
- msgUrl :="http://www.google.com"
- base64UrlEncodedString :=base64.URLEncoding.EncodeToString([]byte(msgUrl))
- fmt.Println("Base64 编码后:", base64UrlEncodedString)
- base64UrlDecodedByte,err := base64.URLEncoding.DecodeString(base64UrlEncodedString)
- if err !=nil {
- log.Panic(err)
- }
- fmt.Println("Base64 解码后的字节数组:", base64UrlDecodedByte)
- fmt.Println("Base64 解码后:", string(base64UrlDecodedByte))
返回打印结果
Base64 编码后: aHR0cDovL3d3dy5nb29nbGUuY29t
Base64 解码后的字节数组: [104 116 116 112 58 47 47 119 119 119 46 103 111 111 103 108 101 46 99 111 109]
Base64 解码后: http://www.google.com
三. 编码原理
1. 将每个字符转成 ASCII 编码 (10 进制)
fmt.Println([]byte(msg)) //[77 97 99]
2. 将 10 进制编码转成 2 进制编码
- fmt.Printf("%b",[]byte(msg))
- //[1001101 1100001 1100011]
补齐 8 位为:
[01001101 01100001 01100011]
3. 将 2 进制编码按照 6 位一组进行平分
010011 010110 000101 100011
4. 转成 10 进制数
- 010011 ==> 1x2^0 + 1x2^1 + 0 + 0 + 1x2^4 = 19
- 010110 ==> 0 + 1x2^1 + 1x2^2 + 0 + 1x2^4 = 22
- 000101 ==> 1x2^0 + 0 + 1 x 2^2 + 0 + 0 + 0 = 5
- 100011 ==> 1x2^0 + 1x2^1 + 0 + 0 + 0 + 1x2^5 = 35
5. 将十进制数作为索引, 从 Base64 编码表中查找字符
19 对应 T
22 对应 W
5 对应 F
35 对应 j (注意是小写)
注意
若文本为 3 个字符, 则刚好编码为 4 个字符长度 (3 8 = 4 6)
若文本为 2 个字符, 则编码为 3 个字符, 尾部用一个 "=" 补齐
若文本为 1 个字符, 则编码为 2 个字符, 尾部用两个 "=" 补齐
四. Base58 代码实现
Base58 是用于 Bitcoin 中使用的一种独特的编码方式, 主要用于产生 Bitcoin 的钱包地址.
相比 Base64,Base58 不使用数字 "0", 字母大写 "O", 字母大写 "I", 和字母小写 "l", 以及 "+" 和 "/" 符号.
设计 Base58 主要的目的是:
避免混淆. 在某些字体下, 数字 0 和字母大写 O, 以及字母大写 I 和字母小写 l 会非常相似.
不使用 "+" 和 "/" 的原因是非字母或数字的字符串作为帐号较难被接受.
没有标点符号, 通常不会被从中间分行.
大部分的软件支持双击选择整个字符串.
但是这个 base58 的计算量比 base64 的计算量多了很多. 因为 58 不是 2 的整数倍, 需要不断用除法去计算.
而且长度也比 base64 稍微多了一点.
附上 golang 的代码实现
- package main
- import (
- "math/big"
- "bytes"
- "fmt"
- )
- func main() {
- /*
- 测试 Base58
- */
- //1. 原始数据
- data1 := []byte{10, 20, 30, 40, 50, 60}
- data1 = append([]byte{0},data1...) // 前缀 + 数据
- fmt.Println("原始数据:",data1)
- //Base58 编码
- encode:=Base58Encode(data1)
- fmt.Println("Base58 编码后:",encode) //[]byte
- fmt.Println(string(encode))
- //Base58 解码
- decode:=Base58Decode(encode)
- fmt.Println("Base58 解码后:",decode)
- // 测试数据
- data2:="wangergou"
- encode2:=Base58Encode([]byte(data2))
- fmt.Println(encode2)
- fmt.Println(string(encode2))
- decode2:=Base58Decode(encode2)
- fmt.Println(decode2)
- fmt.Println(string(decode2))
- fmt.Println(string(decode2[1:])) //wangergou
- }
- //base64
- /*
- ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
- 0(零),O(大写的 o),I(大写的 i),l(小写的 L),+,/
- */
- var b58Alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
- // 字节数组转 Base58, 加密
- func Base58Encode(input []byte) []byte {
- var result [] byte
- x := big.NewInt(0).SetBytes(input)
- //fmt.Println("x:",x)
- base := big.NewInt(int64(len(b58Alphabet)))//58
- //fmt.Println("base:",base)
- zero := big.NewInt(0)
- mod := &big.Int{}
- for x.Cmp(zero) != 0 {
- x.DivMod(x, base, mod)
- //fmt.Println("x:",x,",mod:",mod) //0-57
- result = append(result, b58Alphabet[mod.Int64()])
- //fmt.Println("result:",string(result))
- }
- // 将得到 result 中的数据进行反转
- ReverseBytes(result)
- for b := range input { // 遍历 input 数组: index,value
- if b == 0x00 {
- result = append([]byte{b58Alphabet[0]}, result...)
- } else {
- break
- }
- }
- //1 result
- return result
- }
- //Base58 转字节数组, 解密
- func Base58Decode(input [] byte) []byte {
- result := big.NewInt(0)
- zeroBytes := 0
- for b := range input {
- if b == 0x00 {
- zeroBytes++
- }
- }
- fmt.Println("zeroBytes:",zeroBytes)
- payload := input[zeroBytes:]
- for _, b := range payload {
- charIndex := bytes.IndexByte(b58Alphabet, b)
- result.Mul(result, big.NewInt(58))
- result.Add(result, big.NewInt(int64(charIndex)))
- }
- decoded := result.Bytes()
- decoded = append(bytes.Repeat([]byte{byte(0x00)}, zeroBytes), decoded...)
- return decoded
- }
- // 字节数组反转
- func ReverseBytes(data []byte) {
- for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
- data[i], data[j] = data[j], data[i]
- }
- }
来源: http://blog.51cto.com/clovemfong/2160384