C#之關(guān)于Base64簡單加密與解密方式
Base64屬于簡單加密算法的一種
類似于凱撒密碼【它是一種替換加密的技術(shù)】
Base64字符串由65個(gè)字符組成
- 大寫字母A~Z,
- 小寫字母a~z,
- 數(shù)字0~9,以及三個(gè)特殊字符+、/、=
- 【=“等號”用于補(bǔ)充字符,使Base64字符串長度變成4的倍數(shù)】
規(guī)則
考慮到初始源字符串可能是任何文本編碼的【中文GBK,Unicode,ASCII等】,因此Base64字符串加密只處理字節(jié)數(shù)組【字節(jié)數(shù)組通過encoding.GetBytes(string src)獲得】。
Base64編碼字符串的長度一定是4的倍數(shù)。
Base64要求把每三個(gè)8Bit的字節(jié)轉(zhuǎn)換為四個(gè)6Bit的字節(jié)(3*8 = 4*6 = 24),然后把6Bit再添兩位高位0,組成四個(gè)8Bit的字節(jié),因此每個(gè)Base64字節(jié)的十進(jìn)制范圍為0~63。也就是說,轉(zhuǎn)換后的字符串理論上將要比原來的長1/3。
字節(jié)數(shù)組的長度應(yīng)該是3的倍數(shù),如果這個(gè)條件不能滿足的話,
具體的解決辦法是這樣的:
原文剩余的字節(jié)根據(jù)編碼規(guī)則繼續(xù)單獨(dú)轉(zhuǎn)(1變2,2變3;不夠的位數(shù)用0補(bǔ)全),再用=號補(bǔ)滿4個(gè)字節(jié)。
這就是為什么有些Base64編碼會以一個(gè)或兩個(gè)等號結(jié)束的原因,但等號最多只有兩個(gè)。
因?yàn)橐粋€(gè)原字節(jié)至少會變成兩個(gè)目標(biāo)字節(jié),所以余數(shù)任何情況下都只可能是0,1,2這三個(gè)數(shù)中的一個(gè)。
如果余數(shù)是0的話,就表示原文字節(jié)數(shù)正好是3的倍數(shù)(最理想的情況)。
如果是1的話,轉(zhuǎn)成2個(gè)Base64編碼字符,為了讓Base64編碼是4的倍數(shù),就要補(bǔ)2個(gè)等號;同理,如果是2的話,就要補(bǔ)1個(gè)等號。
6Bit數(shù)字【0~63】映射Base64字符表如下
索引 | 對應(yīng)字符 | 索引 | 對應(yīng)字符 | 索引 | 對應(yīng)字符 | 索引 | 對應(yīng)字符 |
0 | A | 17 | R | 34 | i | 51 | z |
1 | B | 18 | S | 35 | j | 52 | 0 |
2 | C | 19 | T | 36 | k | 53 | 1 |
3 | D | 20 | U | 37 | l | 54 | 2 |
4 | E | 21 | V | 38 | m | 55 | 3 |
5 | F | 22 | W | 39 | n | 56 | 4 |
6 | G | 23 | X | 40 | o | 57 | 5 |
7 | H | 24 | Y | 41 | p | 58 | 6 |
8 | I | 25 | Z | 42 | q | 59 | 7 |
9 | J | 26 | a | 43 | r | 60 | 8 |
10 | K | 27 | b | 44 | s | 61 | 9 |
11 | L | 28 | c | 45 | t | 62 | + |
12 | M | 29 | d | 46 | u | 63 | / |
13 | N | 30 | e | 47 | v | ||
14 | O | 31 | f | 48 | w | ||
15 | P | 32 | g | 49 | x | ||
16 | Q | 33 | h | 50 | y |
測試Base64源程序
新建WinForm應(yīng)用程序Base64EncoderDemo,重命名默認(rèn)的 Form1為FormBase64Encoder,
窗體FormBase64Encoder設(shè)計(jì)如圖:
FormBase64Encoder.cs主要代碼如下
(忽略設(shè)計(jì)器自動(dòng)生成的代碼):
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace Base64EncoderDemo { public partial class FormBase64Encoder : Form { public FormBase64Encoder() { InitializeComponent(); //參考Convert微軟源程序 //https://referencesource.microsoft.com/#mscorlib/system/convert.cs,fc990bd1275d43d6 } private void FormBase64Encoder_Load(object sender, EventArgs e) { rtxtMessage.ReadOnly = true; //編碼格式 cboEncoding.Items.AddRange(new string[] { "ASCII", "Unicode", "UTF-8", "GBK" }); cboEncoding.SelectedIndex = 0; } private void btnClear_Click(object sender, EventArgs e) { rtxtSourceString.Clear(); rtxtBase64String.Clear(); rtxtMessage.Clear(); } /// <summary> /// 顯示提示消息 /// </summary> /// <param name="content"></param> private void DisplayMessage(string content) { if (rtxtMessage.TextLength >= 20480) { rtxtMessage.Clear(); } rtxtMessage.AppendText($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} -> {content}\n"); rtxtMessage.ScrollToCaret(); } private void btnConvertBase64_Click(object sender, EventArgs e) { rtxtBase64String.Clear(); if (rtxtSourceString.Text.Trim().Length == 0) { rtxtSourceString.Focus(); DisplayMessage("源字符串不能為空"); return; } try { Encoding encoding = Encoding.GetEncoding(cboEncoding.Text); byte[] buffer = encoding.GetBytes(rtxtSourceString.Text.Trim()); rtxtBase64String.Text = Convert.ToBase64String(buffer, Base64FormattingOptions.None); DisplayMessage($"轉(zhuǎn)換成功,Base64字符串【{rtxtBase64String.Text}】"); } catch (Exception ex) { DisplayMessage($"轉(zhuǎn)換為Base64時(shí)出錯(cuò):【{ex.Message}】"); } } private void btnRestore_Click(object sender, EventArgs e) { rtxtSourceString.Clear(); if (rtxtBase64String.Text.Trim().Length == 0) { rtxtBase64String.Focus(); DisplayMessage("Base64字符串不能為空"); return; } try { Encoding encoding = Encoding.GetEncoding(cboEncoding.Text); byte[] buffer = Convert.FromBase64String(rtxtBase64String.Text); rtxtSourceString.Text = encoding.GetString(buffer); DisplayMessage($"還原成功,源字符串【{rtxtSourceString.Text}】"); } catch (Exception ex) { DisplayMessage($"還原字符串時(shí)出錯(cuò):【{ex.Message}】"); } } } }
程序運(yùn)行如圖
參考微軟源代碼
public static unsafe String ToBase64String(byte[] inArray, int offset, int length, Base64FormattingOptions options) { //Do data verfication if (inArray==null) throw new ArgumentNullException("inArray"); if (length<0) throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_Index")); if (offset<0) throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_GenericPositive")); if (options < Base64FormattingOptions.None || options > Base64FormattingOptions.InsertLineBreaks) throw new ArgumentException(Environment.GetResourceString("Arg_EnumIllegalVal", (int)options)); Contract.Ensures(Contract.Result<string>() != null); Contract.EndContractBlock(); int inArrayLength; int stringLength; inArrayLength = inArray.Length; if (offset > (inArrayLength - length)) throw new ArgumentOutOfRangeException("offset", Environment.GetResourceString("ArgumentOutOfRange_OffsetLength")); if (inArrayLength == 0) return String.Empty; bool insertLineBreaks = (options == Base64FormattingOptions.InsertLineBreaks); //Create the new string. This is the maximally required length. stringLength = ToBase64_CalculateAndValidateOutputLength(length, insertLineBreaks); string returnString = string.FastAllocateString(stringLength); fixed (char* outChars = returnString){ fixed (byte* inData = inArray) { int j = ConvertToBase64Array(outChars,inData,offset,length, insertLineBreaks); BCLDebug.Assert(returnString.Length == j, "returnString.Length == j"); return returnString; } } }
Base64處理字節(jié)數(shù)組邏輯函數(shù)
internal static readonly char[] base64Table = {'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','+','/','=' }; private const Int32 base64LineBreakPosition = 76;
[System.Security.SecurityCritical] // auto-generated private static unsafe int ConvertToBase64Array(char* outChars, byte* inData, int offset, int length, bool insertLineBreaks) { int lengthmod3 = length%3; int calcLength = offset + (length - lengthmod3); int j=0; int charcount = 0; //Convert three bytes at a time to base64 notation. This will consume 4 chars. int i; // get a pointer to the base64Table to avoid unnecessary range checking fixed(char* base64 = base64Table) { for (i=offset; i<calcLength; i+=3) { if (insertLineBreaks) { if (charcount == base64LineBreakPosition) { outChars[j++] = '\r'; outChars[j++] = '\n'; charcount = 0; } charcount += 4; } outChars[j] = base64[(inData[i]&0xfc)>>2]; outChars[j+1] = base64[((inData[i]&0x03)<<4) | ((inData[i+1]&0xf0)>>4)]; outChars[j+2] = base64[((inData[i+1]&0x0f)<<2) | ((inData[i+2]&0xc0)>>6)]; outChars[j+3] = base64[(inData[i+2]&0x3f)]; j += 4; } //Where we left off before i = calcLength; if (insertLineBreaks && (lengthmod3 !=0) && (charcount == base64LineBreakPosition)) { outChars[j++] = '\r'; outChars[j++] = '\n'; } switch(lengthmod3) { case 2: //One character padding needed outChars[j] = base64[(inData[i]&0xfc)>>2]; outChars[j+1] = base64[((inData[i]&0x03)<<4)|((inData[i+1]&0xf0)>>4)]; outChars[j+2] = base64[(inData[i+1]&0x0f)<<2]; outChars[j+3] = base64[64]; //Pad j+=4; break; case 1: // Two character padding needed outChars[j] = base64[(inData[i]&0xfc)>>2]; outChars[j+1] = base64[(inData[i]&0x03)<<4]; outChars[j+2] = base64[64]; //Pad outChars[j+3] = base64[64]; //Pad j+=4; break; } } return j; } private static int ToBase64_CalculateAndValidateOutputLength(int inputLength, bool insertLineBreaks) { long outlen = ((long)inputLength) / 3 * 4; // the base length - we want integer division here. outlen += ((inputLength % 3) != 0) ? 4 : 0; // at most 4 more chars for the remainder if (outlen == 0) return 0; if (insertLineBreaks) { long newLines = outlen / base64LineBreakPosition; if ((outlen % base64LineBreakPosition) == 0) { --newLines; } outlen += newLines * 2; // the number of line break chars we'll add, "\r\n" } // If we overflow an int then we cannot allocate enough // memory to output the value so throw if (outlen > int.MaxValue) throw new OutOfMemoryException(); return (int)outlen; }
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C# SqlHelper應(yīng)用開發(fā)學(xué)習(xí)
這篇文章主要和大家一起學(xué)習(xí)C# SqlHelper應(yīng)用開發(fā),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01C# Winform實(shí)現(xiàn)圓角無鋸齒按鈕
這篇文章主要為大家詳細(xì)介紹了C# Winform實(shí)現(xiàn)圓角無鋸齒按鈕,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07ZooKeeper 實(shí)現(xiàn)分布式鎖的方法示例
這篇文章主要介紹了ZooKeeper 實(shí)現(xiàn)分布式鎖的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-06-06Unity3D運(yùn)行報(bào)DllNotFoundException錯(cuò)誤的解決方案
這篇文章主要介紹了Unity3D運(yùn)行報(bào)DllNotFoundException錯(cuò)誤的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04C#編程實(shí)現(xiàn)向并口設(shè)備發(fā)送指令、獲取并口設(shè)備的狀態(tài)
這篇文章主要介紹了C#編程實(shí)現(xiàn)向并口設(shè)備發(fā)送指令、獲取并口設(shè)備的狀態(tài),本文直接給出實(shí)例代碼,需要的朋友可以參考下2015-06-06WinForm中變Enter鍵為Tab鍵實(shí)現(xiàn)焦點(diǎn)轉(zhuǎn)移的方法
這篇文章主要介紹了WinForm中變Enter鍵為Tab鍵實(shí)現(xiàn)焦點(diǎn)轉(zhuǎn)移的方法,主要通過一個(gè)ControlTools類來實(shí)現(xiàn)該功能,需要的朋友可以參考下2014-08-08