• C#·排坑·非标准Base64转换失败


    阅文时长 | 1.66分钟 字数统计 | 2664.8字符
    主要内容 | 1、引言&背景 2、验证非标准Base64字符串的判断依据 3、C#解析非标准Base64的方法 4、声明与参考资料
    『C#·排坑·非标准Base64转换失败』
    编写人 | SCscHero 编写时间 | 2021/7/23 PM8:41
    文章类型 | 系列 完成度 | 已完成
    座右铭 每一个伟大的事业,都有一个微不足道的开始。

    一、引言&背景   完成度:100%

    a) 应对问题

    C#中的Convert.FromBase64String()方法转换某些省略尾巴的"="或"=="的非标准Base64编码的字符串,会报错,详细信息如下:

    System.FormatException
      HResult=0x80131537
      Message=Base-64 字符数组或字符串的长度无效。
      Source=mscorlib
      StackTrace:
       在 System.Convert.FromBase64_Decode(Char* startInputPtr, Int32 inputLength, Byte* startDestPtr, Int32 destLength)
       在 System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength)
       在 System.Convert.FromBase64String(String s)
       在 _00056._Console_FW4_7_Csharp解析非标准Base64问题.Program.Main(String[] args) 在 D:\00070.代码仓\00056._Console_FW4_7_Csharp解析非标准Base64问题\Program.cs 中: 第 35 行
    
      此异常最初是在此调用堆栈中引发的: 
        System.Convert.FromBase64_Decode(char*, int, byte*, int)
        System.Convert.FromBase64CharPtr(char*, int)
        System.Convert.FromBase64String(string)
        _00056._Console_FW4_7_Csharp解析非标准Base64问题.Program.Main(string[]) (位于 Program.cs 中)
    

    而在某些语言中,如Java等,原生的方法是支持这种非标准的Base64字符串解析的。但在C#中如果使用Convert.FromBase64String()的原生方法,是严格审查Base64标准的。所以我们需要变通一下,对此种非标准字符串做兼容。

    b) 应对场景

    1. 应用于C#技术栈,解码Base64非标准字符串的场景。
    2. 如遇在线Base64解析网站可以解析出来但C#中的Convert.FromBase64String()方法报Base-64字符数组或字符串的长度无效的场景。

    c) 本文大纲

    1. 原理解析
      • 非标准Base64字符串的判断依据
      • 非标准Base64字符串的产生场景
    2. C#解析非标准Base64的方法
      • 替换&填充法
      • 从生成原理上兼容
      • 使用第三方包中的方法

    二、验证非标准Base64字符串的判断依据   完成度:100%

    a) 非标准Base64字符串判断依据

    一个有效的base64编码字符串的长度应该是可以对4分隔的,并且应该用1或2个填充字符"="来填充。填充是可选的,但C#对base64编码字符串是严格解码,因此需要添加缺失的填充符号。除此之外,还有两个base64的特定字符"_"和"-"需要替换成"/"和"+"。

    b) 非标准Base64字符串的产生场景

    如博主遇到的状况,博主接收到的Base64加密字符串是一个Java程序生成的,Java在加密方法的某个参数设置,会导致非标准的Base64字符串的产生。

    三、C#解析非标准Base64的方法   完成度:100%

    a) 方案1:替换&填充法

    根据非标准Base64字符串的判断依据,我们可以使用如下写法变通:

    var tempStr = base64StrNoStand.Replace('_', '/').Replace('-', '+');
    switch (base64StrNoStand.Length % 4)
    {
        case 2: base64StrNoStand += "=="; break;
        case 3: base64StrNoStand += "="; break;
    }
    byte[] byteArray2 = Convert.FromBase64String(base64StrNoStand);
    Console.WriteLine(Encoding.UTF8.GetString(byteArray2));
    

    b) 方案2:从生成原理兼容

    原理不在深究,博主现阶段主要做上层应用,以下为兼容实现的公用方法,代码如下:

        /// <summary>
        /// Base64解码类
        /// 将Base64编码的string类型转换成byte[]类型
        /// </summary>
        public class Base64Decoder
        {
            char[] source;
            int length, length2, length3;
            int blockCount;
            int paddingCount;
            public static Base64Decoder Decoder = new Base64Decoder();
    
            public Base64Decoder()
            {
            }
    
            private void init(char[] input)
            {
                int temp = 0;
                source = input;
                length = input.Length;
    
                for (int x = 0; x < 2; x++)
                {
                    if (input[length - x - 1] == '=')
                        temp++;
                }
    
                paddingCount = temp;
    
                blockCount = length / 4;
                length2 = blockCount * 3;
            }
    
            public byte[] GetDecoded(string strInput)
            {
                //初始化
                init(strInput.ToCharArray());
    
                byte[] buffer = new byte[length];
                byte[] buffer2 = new byte[length2];
    
                for (int x = 0; x < length; x++)
                {
                    buffer[x] = char2sixbit(source[x]);
                }
    
                byte b, b1, b2, b3;
                byte temp1, temp2, temp3, temp4;
    
                for (int x = 0; x < blockCount; x++)
                {
                    temp1 = buffer[x * 4];
                    temp2 = buffer[x * 4 + 1];
                    temp3 = buffer[x * 4 + 2];
                    temp4 = buffer[x * 4 + 3];
    
                    b = (byte) (temp1 << 2);
                    b1 = (byte) ((temp2 & 48) >> 4);
                    b1 += b;
    
                    b = (byte) ((temp2 & 15) << 4);
                    b2 = (byte) ((temp3 & 60) >> 2);
                    b2 += b;
    
                    b = (byte) ((temp3 & 3) << 6);
                    b3 = temp4;
                    b3 += b;
    
                    buffer2[x * 3] = b1;
                    buffer2[x * 3 + 1] = b2;
                    buffer2[x * 3 + 2] = b3;
                }
    
                length3 = length2 - paddingCount;
                byte[] result = new byte[length3];
    
                for (int x = 0; x < length3; x++)
                {
                    result[x] = buffer2[x];
                }
    
                return result;
            }
    
            private byte char2sixbit(char c)
            {
                char[] lookupTable = new char[64]
                {
                    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
                    'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
                    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
                    'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
                    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
                };
                if (c == '=')
                    return 0;
                else
                {
                    for (int x = 0; x < 64; x++)
                    {
                        if (lookupTable[x] == c)
                            return (byte) x;
                    }
    
                    return 0;
                }
            }
        }
    

    c) 使用第三方包中的方法

    如第三方包newtonsoft.json中的解码Base64的方法。

    四、声明与参考资料   完成度:100%

    原创博文,未经许可请勿转载。

    如有帮助,欢迎点赞、收藏、关注。如有问题,请评论留言!如需与博主联系的,直接博客私信SCscHero即可。

  • 相关阅读:
    移动端阻止事件冒泡需要注意!
    JavaScript闭包
    JS原型与原型链终极讲解
    逐行分析jQuery源码
    jQuery源码分析-03构造jQuery对象-源码结构和核心函数
    screenX clientX pageX的区别
    JSON.stringify 语法实例讲解
    pipenv 简要指南
    webpy简单使用
    初识Dash -- 构建一个人人都能够轻松上手的界面,操控数据和可视化
  • 原文地址:https://www.cnblogs.com/SCscHero/p/15054051.html
Copyright © 2020-2023  润新知