使用 C# 实现 XOR 密码加密/解密






4.15/5 (13投票s)
XOR 加密技术在 C# 中的理论和实践实现,该技术源于 Vernam 密码。
下载 Binary_XOREncryptionDecryption.zip - 5.01 KB
下载 Project_XOREncryptionDecryption.zip - 16.35 KB
使用 C# 实现 XOR 密码加密/解密
对 XOR 加密技术在 C# 中的理论和实践实现,该技术源自 Vernam 密码。
引言
XOFT 是一种加密算法,它使用密钥与密文进行混合。其结果经过 base64 编码。它肯定不是最强大的加密技术;然而,如果 100% 的安全性不是首要目标,它就足够了。
密钥越长,破解密钥所需的时间就越长。然而,密文越长,解码字符串就越容易,因为使用频率分析更容易发现编码字符串中的重复模式。
最佳解决方案是使密钥的长度与源字符串的长度相同。这不仅在理论上可以实现无法破解的加密。只有当源字符串的长度始终相同(例如,GUID)时,才有可能做到这一点。当加密纯文本或长度可变的文本时,这并不实用,因为解码端和编码端必须知道密钥。
思考
延长安全性的一个想法是创建一个动态密钥,该密钥与源数据的长度相关。
解码器将知道源数据的长度,因为它总是比 base64 编码的结果短 2 倍。(我们将 8 位(字节)二进制块,并将它们分成 4 位(半字节)二进制块。)这样做,我们从一个字节创建两个字符。
-> 解码器和编码器可以使用某些内部算法,根据密文的长度始终生成相同的密钥。
为了保持加密的足够复杂性,动态密钥在理论上是一个有趣的想法,但我不会在本文中实现它。
数据如何编码
我现在将详细地重建编码,基于理论和实践基础——解码将以相同的方式进行,但颠倒过来。
在我们的例子中,我们将使用两个字符串。一个是我们要加密的源(“hello world”),另一个字符串是密钥(“hidden”)。编码器和解码器都必须知道密钥。
• 我们将把密钥和源都转换为字符数组(ASCII)。• 源的每个字符将与密钥的每个字符进行 XOR 运算。
• 结果将加上密钥的长度(“hidden”有 6 个字符)。
• 如果密钥比源短,它将被循环使用。
• 现在我们将结果值转换为一个字节的二进制值。
• 这些二进制字符串将被连接起来,并分成半字节。
• 4 位块将被转换回十进制值。
• 然后我们将十进制结果数组中的每个项乘以 4 并加上 m=m0,m1,m2,m3。
• 接近完成,现在我们将每个十进制值转换为 base64 值。
加密的理论示例
为了方便起见,我们取一个简短的源字符串和一个简短的密钥。
源 = “hello world”
密钥 = “hidden”
将其分解为字符
来源h = 104
e = 101
l = 108
l = 108
o = 111
= 32
w = 119
o = 111
r = 114
l = 108
d = 100
键h = 104
i = 105
d = 100
d = 100
e = 101
n = 110
现在我们将进行 XOR 运算并加上 4。
我们取 (source[n] ^ key[n1]) + 4,其中 n1 是一个循环值 (n1 < key.Length < n1)。这将密钥混合到源字符串中。仅有的 3 种方法可以再次分离它们是:
- 知道密钥以获取源
- 知道源以获取密钥
- 使用暴力破解来获取两者
(104 ^ 104) + 6 = 6
(101 ^ 105) + 6 = 18
(108 ^ 100) + 6 = 14
(108 ^ 100) + 6 = 14
(111 ^ 101) + 6 = 16
(32 ^ 110) + 6 = 84
(119 ^ 104) + 6 = 37
(111 ^ 105) + 6 = 12
(114 ^ 100) + 6 = 28
(108 ^ 100) + 6 = 14
(100 ^ 101) + 6 = 7
转换为二进制
在我们将源/密钥混合后,我们将其转换为二进制值。
6 = 00000110
18 = 00010010
14 = 00001110
14 = 00001110
16 = 00010000
84 = 01010100
37 = 00100101
12 = 00001100
28 = 00011100
14 = 00001110
7 = 00000111
创建半字节块并将其转换为 base64 序列。
首先,我们需要将二进制字节值转换为一个大的序列(字符串)。然后我们将这个字符串分成 4 个字符的序列,这些就是我们的半字节。
00000110000100100000111000001110000100000101 01000010010100001100000111000000111000000111
将其分解为半字节如下:
0000 0110 0001 0010 0000 1110 0000 1110 0001 0000 0101
0100 0010 0101 0000 1100 0001 1100 0000 1110 0000 0111
并转换回十进制值:
0 6 1 2 0 14 0 14 1 0 5 4 2 5 0 12 1 12 0 14 0 7
现在我们将每个值乘以 4 并加上 m,其中 m 是一个 0,1,2,3 的序列。
0*4+0, 6*4+1, 1*4+2, 2*4+3, 0*4+0, 14*4+1, 0*4+2, 14*4+3, 1*4+0, 0*4+1, 5*4+2, 4*4+3, 2*4+0, 5*4+1, 0*4+2, 12*4+3, 1*4+0, 12*4+1, 0*4+2, 14*4+3, 0*4+0, 7*4+1
这最终将得到这些值,然后转换为 base64:
0 25 6 11 0 57 2 59 4 1 22 19 8 21 2 51 4 49 2 59 0 29
A Z G L A 5 C 7 E B W T I V C z E x C 7 A d
代码输出
这是 base64 编码的字符串,它是由我们的源“hello world”和我们的密钥“hidden”组成的。
AZGLA5C7EBWTIVCzExC7Ad
小助手
您可能已经注意到我们进行了一些从整数到 base64 再到整数的转换,以及从整数到二进制再到整数的转换。对于这些大部分,没有原生的 C# 代码(据我所知),所以这里是用于转换这些类型的函数。我在互联网上找到了一些相当复杂的函数,但我几乎可以肯定以下两个函数已经足够优化。
base64 功能的代码
可能存在更好的实现……
// this is the base64 alphabet – depending on // the standard that last characters (+/=) can vary // possibile options are (-_%) (!*_) etc... private static string _b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; // expects a base64 character and returns it's position in the base64 alphabet private static int GetNFromB64(char n) { return _b64.IndexOf(n); }
// expects a position in the base64 // alphabet and returns it's base64 character. private static string GetB64FromN(int n) { if (n > _b64.Length) return "="; return new string(_b64[n], 1); }
二进制功能代码
也可能存在更好的实现。我仍然不明白为什么 .NET 为我们提供了将二进制字符串转换为整数的选项,却没有简单的方法将整数转换为二进制字符串……但我缺少一个好的 string.Reverse() 函数。
DecToBinary 是从互联网上多个作者的代码片段派生出来的。所以很遗憾我无法为此提供适当的署名。
// expects a integer value and length of the binary string (e.g. 4, 8, 16).
// returns the padded binary string
private static string DecToBinary(int value, int length)
{
// Declare a few variables we're going to need
string binString = "";
while (value > 0)
{
binString += value % 2;
value /= 2;
}
// we need to reverse the binary string
// that's why we are using this array stuff here.
string reverseString = "";
foreach (char c in binString)
reverseString = new string((char)c, 1) + reverseString;
binString = reverseString;
// do the padding here
binString = new string((char)'0', length - binString.Length) + binString;
return binString;
}
// expects the binary string and returns it's integer equivalent
private static int BinToDec(string Binary)
{
return Convert.ToInt32(Binary, 2);
}