【转】Golang base64 解码碰到的坑

背景
在一次调用三方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)
    }
}
————————————————
版权声明:本文为CSDN博主「只会打野怪我咯」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u014270740/article/details/91038606

执行结果

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)
    }
}

————————————————
版权声明:本文为CSDN博主「只会打野怪我咯」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u014270740/article/details/91038606

执行一下 又报错了,心累,看了下错误信息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)
    }
}
————————————————
版权声明:本文为CSDN博主「只会打野怪我咯」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u014270740/article/details/91038606

加了两个等号,居然解码成功了,太不容易了,但是这问题也来了, 这追加的等号也不是固定的,如果在代码里面判断增加也很不方便啊,刚尝到查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)
    }
}
————————————————
版权声明:本文为CSDN博主「只会打野怪我咯」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u014270740/article/details/91038606

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

 

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

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

ps: 作者遇到的问题其实是稳定的前端输入,如果我们的前端输入可能带来不同的 encode 方式,那么我们可能并无法只兼容最后一种可能就可以,而是需要每种解析都遍历到,留下没有 error 的结果即可。

func ParseB64String(b64String string) ([]byte, error) {
    missingPadding := len(b64String) % 4
    if missingPadding != 0 {
        b64String = b64String + strings.Repeat("=", missingPadding)
    }
    decodedBytes, err := base64.RawURLEncoding.DecodeString(b64String)
    if err != nil {
        decodedBytes, err = base64.URLEncoding.DecodeString(b64String)
        if err != nil {
            decodedBytes, err = base64.StdEncoding.DecodeString(b64String)
            if err != nil {
                logs.Warning.Println("decode base64 fail:", err.Error())
                return []byte{}, err
            }
        }
    }

    return decodedBytes, nil
}

 

 

Reference:

https://blog.csdn.net/u014270740/article/details/91038606

 

posted @ 2023-03-08 16:46  piperck  阅读(1662)  评论(1编辑  收藏  举报