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即可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM