C#Base64 simple encryption and decryption

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

Keywords: C# Algorithm .NET

Added by nickthegreek on Fri, 10 Dec 2021 14:25:12 +0200