Base64 is a simple encryption algorithm. Similar to Caesar's password [it's an alternative encryption technology]
Base64 string consists of 65 characters,
Capital letters A~Z,
Lowercase a~z,
Numbers 0 to 9, and three special characters +, /, =
[= "equal sign" is used to supplement characters to make Base64 string length a multiple of 4]
rule
Considering that the initial source string may be any text encoded [Chinese GBK, Unicode, ASCII, etc.), Base64 string encryption only processes byte array [byte array is obtained through encoding.GetBytes(string src)].
The length of Base64 encoded string must be a multiple of 4.
Base64 requires converting every three 8Bit bytes into four 6Bit bytes (3 * 8 = 4 * 6 = 24), and then adding two higher bits of 0 to 6Bit to form four 8Bit bytes. Therefore, the decimal range of each Base64 byte is 0 ~ 63. In other words, the converted string will theoretically be 1 / 3 longer than the original string.
The length of the byte array should be a multiple of 3. If this condition cannot be met, the specific solution is as follows: the remaining bytes of the original text continue to be converted separately according to the coding rules (1 to 2, 2 to 3; the insufficient bits are filled with 0), and then fill up 4 bytes with the = sign. This is why some Base64 codes end with one or two equal signs, but there are only two equal signs at most. Because an original byte will become at least two target bytes, the remainder can only be one of the three numbers 0, 1 and 2 in any case. If the remainder is 0, it means that the number of original bytes is exactly a multiple of 3 (the most ideal case). If it is 1, it is converted to 2 Base64 encoded characters. In order to make Base64 encoding a multiple of 4, two equal signs must be added; Similarly, if it is 2, you have to fill in an equal sign.
6Bit number [0 ~ 63] mapping Base64 character table is as follows
Indexes | Corresponding character | Indexes | Corresponding character | Indexes | Corresponding character | Indexes | Corresponding character |
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 |
Test Base64 source program
Create a new WinForm application base64encoder demo, rename the default Form1 to FormBase64Encoder,
The form FormBase64Encoder design is shown in the figure below:
FormBase64Encoder.cs main codes are as follows
(ignore code automatically generated by the designer):
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(); //Refer to Convert Microsoft source program //https://referencesource.microsoft.com/#mscorlib/system/convert.cs,fc990bd1275d43d6 } private void FormBase64Encoder_Load(object sender, EventArgs e) { rtxtMessage.ReadOnly = true; //Coding format 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> ///Display prompt message /// </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("The source string cannot be empty"); return; } try { Encoding encoding = Encoding.GetEncoding(cboEncoding.Text); byte[] buffer = encoding.GetBytes(rtxtSourceString.Text.Trim()); rtxtBase64String.Text = Convert.ToBase64String(buffer, Base64FormattingOptions.None); DisplayMessage($"Conversion succeeded, Base64 String[{rtxtBase64String.Text}]"); } catch (Exception ex) { DisplayMessage($"Convert to Base64 Error during:[{ex.Message}]"); } } private void btnRestore_Click(object sender, EventArgs e) { rtxtSourceString.Clear(); if (rtxtBase64String.Text.Trim().Length == 0) { rtxtBase64String.Focus(); DisplayMessage("Base64 String cannot be empty"); return; } try { Encoding encoding = Encoding.GetEncoding(cboEncoding.Text); byte[] buffer = Convert.FromBase64String(rtxtBase64String.Text); rtxtSourceString.Text = encoding.GetString(buffer); DisplayMessage($"Restore succeeded, source string[{rtxtSourceString.Text}]"); } catch (Exception ex) { DisplayMessage($"Error restoring string:[{ex.Message}]"); } } } }
The program runs as shown in the figure below:
Refer to Microsoft source code: Reference Source
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 processing byte array logic function
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; }