C#入门实践:Windows桌面加密器 CryptoSharp
Step 1 BASE64/编码/DLL
by Pixel Frame
GitHub: CryptoSharp
源码在文中基本不再出现。
0x00 前期准备
本着由浅入深、由简入繁、Top Down的原则,首先为Visual Studio选择一个好看的配色方案和合适的字体(什么狗P逻辑)。
个人还是比较喜欢深色配色,奈何Color Theme Editor插件中没有Monokai配色,自己改一个又太累了(),所以直接选择了Dark with Light Editor。
字体上的话,用Font Link把Consolas和微软雅黑链接是一个简单快捷的选择。思源的等宽字体感觉太宽了,和中文混排看起来十分诡异。原生的中英文混排等宽字体其实非常少,比如说新宋体()。首推的还是Belleve大的更纱黑体,还有一个经典选择自然是文泉驿等宽微米黑和文泉驿等宽正黑。
0x01 BASE64与编码
作为一个加密器自然需要考虑各种各样的编码问题,虽然说也有很多场合把BASE64称作加密,但其本质只是把不可显示的ASCII码转换到了可显示部分。C#的Convert类中自带了BASE64转换,所以可以直接调用(本篇完,下面没有了)。
然而,要自己实现一遍~(我写的比库函数好得多.png)。
C#所有文本都是Unicode(UTF-16LE),即对应C++中的16位字符wchar_t
,不能和C++一样将工程设置为多字节(UTF-8/ANSI等)。这对于进行加解密操作其实是不太方便的,BASE64是针对8位的byte(unsigned char)
进行操作的,因此我们需要自己构建一个C++的std::string
来方便操作。当然,加密算法中大量bit级的操作其实都不适合软件编程,相比之下用HDL作专用硬件会方便很多。关于位操作的问题会在DES中更多涉及。
ByteString
一个好用的string
类需要哪些东西是很显然的,动态分配/截取/查找/运算符重载...当然,我的目标是够用就行,需要新的功能时再行添加。C#的List<>
和数组十分强大,可以直接通过AddRange(), ToArray(), ToList()
等一系列方法进行转换和增加,因此其实就是对List<byte>
做一个二次封装。
用List<byte>
保存字符数据,重载+
运算符,构造函数,定义索引器[]
,外加Set/GetBytes()
和ToString()
方法。这样一个简单可用的ByteString
类就完成了。
在之后可能还会加入移位等的功能,在遇到时再行讨论。
BASE64
BASE64的加解密并不复杂,简单来说就是每三个bytes进行一次替换,不足的地方用=
替换。那么,这里就直接从一份C++代码进行改写。将C++中的char
全部改为byte
类型,通过Encoding
获取字符的ASCII码,这里只要采用多字节代码页(ANSI/UTF-8/GBK/BIG5)都是没有问题的,结果都是相同的ASCII码。当然Encoding
会损失一定的性能,通过硬编码将对应ASCII码写入程序能提高一定的性能,当然既然选择了C#对性能也不做过分的追求了。
一般而言会将查找表设为全局常量,然而并没有找到如何声明一个const
数组,那么就只好使用static readonly
,效果似乎是相同的。
static readonly byte[] base64Table = Encoding.UTF8.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +/");
static readonly byte base64Pad = Encoding.UTF8.GetBytes("=")[0];
static readonly byte[] base64PadD = Encoding.UTF8.GetBytes("==");
static readonly int[] decodeTable =
{
-2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -2, -1, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -2, -2, -2,
-2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2,
-2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
};
加解密过程就不作详细解释了,再来说说编码的问题。基本的加密方法的输入是byte[]
输出是string(UTF-16LE)
,但一般而言输入会是一个字符串并且会有各种编码的可能(当然作为C#程序一般获取到的都会是UTF-16LE)。高级语言的好处就在于各种功能都有现成的封装,相比C++的MultiByteToWideChar()
函数和USES_CONVERSION
宏,C#的Encoding
类要方便很多,支持的编码可以在微软的开发文档中找到。核心的Encrypt
和Decrypt
方法使用byte[]
作为输入/输出,再进行string
的重载并且添加指定编码参数。
public static String Encrypt(byte[] bPlain);
public static String Encrypt(String strPlain, String coding);
public static String Encrypt(String strPlain, int coding);
public static byte[] Decode(String strEnc);
public static String DecodeEncoding(String strEnc, String coding);
public static String DecodeEncoding(String strEnc, int coding);
0x02 解决方案与DLL
为了方便管理,将各个加密算法作为DLL项目添加至解决方案中。DLL的接口方法会逐渐完善。最终CryptoSharp将只需要调用DLL的接口,本身则是WinForm UI为主。
C#中同一解决方案项目、.NET库、外部DLL都只需要在添加引用即可,包括C++ COM组件DLL也是可以直接使用的。
次回予告:DES AND SHIFTING
END_OF_PART_2
网友评论