背景

     在一次调用三方API的时候,为了数据安全和三方API的请求和返回信息都用了rsa加密,由于rsa算法生成的内容是二进制的,所以需要用base64编码将二进制数据转化成64个可打印字符进行通信或者存储。在获取到三方的base64编码的返回信息后,解码碰到了各种问题,如下是日志里打印的三方的一个base64编码后的字符串

Cf1WA2nBMo3H9G2UPhlLBBVBsMDl4udWr7__e6Iy93eIqLKi3EOjGhk8TkHujL1Uj6aGfZJNBzIbVE2NfNaz4pob8uiQvGaeTZdWP-8lFmAm6J1sz8N15xQkO7ADa5bNLCCqtlQbN2z7JcNenvFuID_rZGqb_1gmr-BGubGRMiMSK7RdjQYrMHaBcHLPB0UteakzcQwgKxCW7u0ECHqPJ39ne9JUG22JBWRo1ORuX5r30J_XrW3SQcdPSxfe0kvd61y12QOYh8VlOBBdBeDNnyDXefI_tDJDBFeqTXCgKu9wFkkWIZiM7WwqogaY-bvjUisbrPO4_fjJ1c0nWDOqRA

解码过程

1.使用标准库的base64.StdEncoding.DecodeString

  最开始是印象标准库有一个base64.StdEncoding.DecodeString方法可以解码,就直接使用了这个方法

package main

import(
	"encoding/base64"
	"fmt"
)

func main(){
	encrypt := "Cf1WA2nBMo3H9G2UPhlLBBVBsMDl4udWr7__e6Iy93eIqLKi3EOjGhk8TkHujL1Uj6aGfZJNBzIbVE2NfNaz4pob8uiQvGaeTZdWP-8lFmAm6J1sz8N15xQkO7ADa5bNLCCqtlQbN2z7JcNenvFuID_rZGqb_1gmr-BGubGRMiMSK7RdjQYrMHaBcHLPB0UteakzcQwgKxCW7u0ECHqPJ39ne9JUG22JBWRo1ORuX5r30J_XrW3SQcdPSxfe0kvd61y12QOYh8VlOBBdBeDNnyDXefI_tDJDBFeqTXCgKu9wFkkWIZiM7WwqogaY-bvjUisbrPO4_fjJ1c0nWDOqRA"
	_,err := base64.StdEncoding.DecodeString(encrypt)
	if err != nil {
		fmt.Println(err)
	}
}

执行结果

illegal base64 data at input byte 34
解码报错了,根据提示意思大概能猜到是第34位的字符‘_’ base64不认识,去查了下base64的索引表

发现base64的字符集内没有我们第34位对应的字符‘_’,当时想是不是做了类似urlencode的编码,防止http传输过程中部分字符转义,继续查base64文档发现了 如下一段内容

可以确定是将+和/分别改成了-和_, 那我们应该做一下字符串替换把-和_改回来就行,本来想直接在代码里面写字符串替换,后来想GO标准库还没细看,里面是不是还有现成的方法之前没发现,查了下标准库发现

URLEncoding和我们刚用的StdEncoding有一些区别,是用于URL和文件名,刚我们碰到的问题也是URL问题

2.用base64.URLEncoding.DecodeString方法尝试

  经过了上一步的测试和文档查找,感觉用这个方法应该就可以搞定问题了,赶紧写个测试看看

package main

import(
	"encoding/base64"
	"fmt"
)

func main(){
	encrypt := "Cf1WA2nBMo3H9G2UPhlLBBVBsMDl4udWr7__e6Iy93eIqLKi3EOjGhk8TkHujL1Uj6aGfZJNBzIbVE2NfNaz4pob8uiQvGaeTZdWP-8lFmAm6J1sz8N15xQkO7ADa5bNLCCqtlQbN2z7JcNenvFuID_rZGqb_1gmr-BGubGRMiMSK7RdjQYrMHaBcHLPB0UteakzcQwgKxCW7u0ECHqPJ39ne9JUG22JBWRo1ORuX5r30J_XrW3SQcdPSxfe0kvd61y12QOYh8VlOBBdBeDNnyDXefI_tDJDBFeqTXCgKu9wFkkWIZiM7WwqogaY-bvjUisbrPO4_fjJ1c0nWDOqRA"
	_,err := base64.URLEncoding.DecodeString(encrypt)
	if err != nil {
		fmt.Println(err)
	}
}

执行一下 又报错了,心累,看了下错误信息illegal base64 data at input byte 340,跟第一步报错的大致一样只是最后的位置变到了340,说明我们前面转义的问题用这个方法还是解决了,但是执行到最后的时候又有不标准字符了,继续查base64的文档,又有新发现,如图

大致意思是,如果编码的时候字节不足会在最后加一到两个=号,但看我们的字符串最后没有=,解码的时候解到最后又报错了,赶紧给字符串手动加个等号试试

package main

import(
	"encoding/base64"
	"fmt"
)

func main(){
	encrypt := "Cf1WA2nBMo3H9G2UPhlLBBVBsMDl4udWr7__e6Iy93eIqLKi3EOjGhk8TkHujL1Uj6aGfZJNBzIbVE2NfNaz4pob8uiQvGaeTZdWP-8lFmAm6J1sz8N15xQkO7ADa5bNLCCqtlQbN2z7JcNenvFuID_rZGqb_1gmr-BGubGRMiMSK7RdjQYrMHaBcHLPB0UteakzcQwgKxCW7u0ECHqPJ39ne9JUG22JBWRo1ORuX5r30J_XrW3SQcdPSxfe0kvd61y12QOYh8VlOBBdBeDNnyDXefI_tDJDBFeqTXCgKu9wFkkWIZiM7WwqogaY-bvjUisbrPO4_fjJ1c0nWDOqRA"
	//为了不修改原字符串,没有直接在原字符串上追加
	encrypt = fmt.Sprint(encrypt,"==")
	_,err := base64.URLEncoding.DecodeString(encrypt)
	if err != nil {
		fmt.Println(err)
	}
}

加了两个等号,居然解码成功了,太不容易了,但是这问题也来了, 这追加的等号也不是固定的,如果在代码里面判断增加也很不方便啊,刚尝到查GO文档的好处了,带着问题找一个可以忽略最后等号的方法,看下GO文档里有没有,

3.使用base64.RawURLEncondig.DecodeString

前一个步骤我们得到的结论要在GO文档中找一个可以将-_还原成base64字符的同时可以让我们忽略末尾=号的,嗯  还真找到了一个base64.RawURLEncoding.DecodeString,赶紧试下

package main

import(
	"encoding/base64"
	"fmt"
)

func main(){
	encrypt := "Cf1WA2nBMo3H9G2UPhlLBBVBsMDl4udWr7__e6Iy93eIqLKi3EOjGhk8TkHujL1Uj6aGfZJNBzIbVE2NfNaz4pob8uiQvGaeTZdWP-8lFmAm6J1sz8N15xQkO7ADa5bNLCCqtlQbN2z7JcNenvFuID_rZGqb_1gmr-BGubGRMiMSK7RdjQYrMHaBcHLPB0UteakzcQwgKxCW7u0ECHqPJ39ne9JUG22JBWRo1ORuX5r30J_XrW3SQcdPSxfe0kvd61y12QOYh8VlOBBdBeDNnyDXefI_tDJDBFeqTXCgKu9wFkkWIZiM7WwqogaY-bvjUisbrPO4_fjJ1c0nWDOqRA"
	_,err := base64.RawURLEncoding.DecodeString(encrypt)
	if err != nil {
		fmt.Println(err)
	}
}

执行下,终于完美成功了。 

总结

     base64编码过程有两部特殊操作

  •    url safe 将+/字符串转化成_-
  •    no padding is add  末尾不增加=号