C#之关于Base64简单加密与解密方式

 更新时间:2023年06月16日 16:56:49   作者:斯内科  
这篇文章主要介绍了C#之关于Base64简单加密与解密方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

Base64属于简单加密算法的一种

类似于凯撒密码【它是一种替换加密的技术

Base64字符串由65个字符组成

  • 大写字母A~Z,
  • 小写字母a~z,
  • 数字0~9,以及三个特殊字符+、/、=  
  • 【=“等号”用于补充字符,使Base64字符串长度变成4的倍数】

规则

考虑到初始源字符串可能是任何文本编码的【中文GBK,Unicode,ASCII等】,因此Base64字符串加密只处理字节数组【字节数组通过encoding.GetBytes(string src)获得】。

Base64编码字符串的长度一定是4的倍数。

Base64要求把每三个8Bit的字节转换为四个6Bit的字节(3*8 = 4*6 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,因此每个Base64字节的十进制范围为0~63。也就是说,转换后的字符串理论上将要比原来的长1/3。

字节数组的长度应该是3的倍数,如果这个条件不能满足的话,

具体的解决办法是这样的:

原文剩余的字节根据编码规则继续单独转(1变2,2变3;不够的位数用0补全),再用=号补满4个字节。

这就是为什么有些Base64编码会以一个或两个等号结束的原因,但等号最多只有两个。

因为一个原字节至少会变成两个目标字节,所以余数任何情况下都只可能是0,1,2这三个数中的一个。

如果余数是0的话,就表示原文字节数正好是3的倍数(最理想的情况)。

如果是1的话,转成2个Base64编码字符,为了让Base64编码是4的倍数,就要补2个等号;同理,如果是2的话,就要补1个等号。 

6Bit数字【0~63】映射Base64字符表如下

索引

对应字符

索引

对应字符

索引

对应字符

索引

对应字符

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应用程序Base64EncoderDemo,重命名默认的 Form1为FormBase64Encoder,

窗体FormBase64Encoder设计如图:

FormBase64Encoder.cs主要代码如下

(忽略设计器自动生成的代码):

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($"转换成功,Base64字符串【{rtxtBase64String.Text}】");
            }
            catch (Exception ex)
            {
                DisplayMessage($"转换为Base64时出错:【{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($"还原字符串时出错:【{ex.Message}】");
            }
        }
    }
}

程序运行如图

参考微软源代码

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处理字节数组逻辑函数

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

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • C#中enum和string的相互转换

    C#中enum和string的相互转换

    这篇文章主要介绍了C#中enum和string的相互转换的相关资料,需要的朋友可以参考下
    2017-09-09
  • vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

    vista和win7在windows服务中交互桌面权限问题解决方法:穿透Session 0 隔离

    服务(Service)对于大家来说一定不会陌生,它是Windows 操作系统重要的组成部分。我们可以把服务想像成一种特殊的应用程序,它随系统的“开启~关闭”而“开始~停止”其工作内容,在这期间无需任何用户参与
    2016-04-04
  • C#图像对比度调整的方法

    C#图像对比度调整的方法

    这篇文章主要介绍了C#图像对比度调整的方法,涉及C#实现图像对比度操作的相关技巧,需要的朋友可以参考下
    2015-04-04
  • C#实现Quartz任务调度的示例代码

    C#实现Quartz任务调度的示例代码

    使用 Quartz.NET,你可以很容易地安排任务在应用程序启动时运行,或者每天、每周、每月的特定时间运行,甚至可以基于更复杂的调度规则,本文给大家介绍了C#实现Quartz任务调度,需要的朋友可以参考下
    2024-04-04
  • C# networkcomms 3.0实现模拟登陆总结

    C# networkcomms 3.0实现模拟登陆总结

    这篇文章主要介绍了C# networkcomms 3.0实现模拟登陆总结,需要的朋友可以参考下
    2017-06-06
  • C#单例模式Singleton的实现详解

    C#单例模式Singleton的实现详解

    单例模式(Singleton Pattern)是日常开发中最简单的设计模式之一,它提供了一种创建对象的最佳方式,本文主要为大家介绍的是C#单例模式的实现方法,需要的可以参考一下
    2023-05-05
  • C#实现由四周向中心缩小的窗体退出特效

    C#实现由四周向中心缩小的窗体退出特效

    这篇文章主要介绍了C#实现由四周向中心缩小的窗体退出特效,通过简单的C#窗口调用参数设置实现该退出特效功能,非常简单实用,需要的朋友可以参考下
    2015-08-08
  • C#递归方法实现无限级分类显示效果实例

    C#递归方法实现无限级分类显示效果实例

    这篇文章主要介绍了C#递归方法实现无限级分类显示效果,结合完整实例形式分析了C#递归算法与数据元素遍历的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2016-06-06
  • 如何在C#中使用Dapper ORM

    如何在C#中使用Dapper ORM

    这篇文章主要介绍了如何在C#中使用Dapper ORM,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下
    2021-03-03
  • 基于C#实现简单离线注册码生成与验证

    基于C#实现简单离线注册码生成与验证

    本文使用RSA非对称加密和Base64简单地实现离线注册码的生成与验证功能。感兴趣的朋友跟着小编一起学习吧
    2015-09-09

最新评论