Value |
|||||||
0 | A | 16 | Q | 32 | g | 48 | w |
1 | B | 17 | R | 33 | h | 49 | x |
2 | C | 18 | S | 34 | i | 50 | y |
3 | D | 19 | T | 35 | j | 51 | z |
4 | E | 20 | U | 36 | k | 52 | 0 |
5 | F | 21 | V | 37 | l | 53 | 1 |
6 | G | 22 | W | 38 | m | 54 | 2 |
7 | H | 23 | X | 39 | n | 55 | 3 |
8 | I | 24 | Y | 40 | o | 56 | 4 |
9 | J | 25 | Z | 41 | p | 57 | 5 |
10 | K | 26 | a | 42 | q | 58 | 6 |
11 | L | 27 | b | 43 | r | 59 | 7 |
12 | M | 28 | c | 44 | s | 60 | 8 |
13 | N | 29 | d | 45 | t | 61 | 9 |
14 | O | 30 | e | 46 | u | 62 | + |
15 | P | 31 | f | 47 | v | 63 | / |
如果要编码的字节数不能被3整除,最后会多出1个或2个字节,那么可以使用下面的方法进行处理:先使用0字节值在末尾补足,使其能够被3整除,然后再进行base64的编码。在编码后的base64文本后加上一个或两个'='号,代表补足的字节数。也就是说,当最后剩余一个八位字节(一个byte)时,最后一个6位的base64字节块有四位是0值,最后附加上两个等号;如果最后剩余两个八位字节(2个byte)时,最后一个6位的base字节块有两位是0值,最后附加一个等号。
比如说这个例子,编码”Man”(ASCII)
再比如编码“A”和“BC”(ASCII)
这个用解释性语言很容易实现的,当然,如果是最底层的语言就更容易实现了。我这里给一下C++的代码
#include <iostream> #include <cstring> #include <string> #include <cstdio> using namespace std; const string base64Tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; string encodePass(string osrc) { string res; int sz = osrc.size(); for (int i = 0; i < sz; i += 3) { string in; int len = 3; if (sz - i == 1) { len = 1; in += osrc[sz - 1]; in += '\0'; in += '\0'; } else if (sz - i == 2) { in = osrc.substr(i, 2); in += '\0'; len = 2; } else in = osrc.substr(i, 3); char ch3 = base64Tab[ in[0] >> 2 ]; char ch4 = base64Tab[ ((in[0] << 4) | (in[1] >> 4)) % 64 ]; char tch3 = base64Tab[ ((in[1] << 2) | (in[2] >> 6)) % 64 ]; char tch4 = base64Tab[ in[2] ]; char ch3 = (len > 1)? tch3: '='; char ch4 = (len > 2)? tch4: '='; res += ch3; res += ch4; res += ch3; res += ch4; } return res; } string decodePass(string osrc) { string res; basic_string<int> oindex; int sz = osrc.size(); for (int i = 0; i < sz; i++) { int t = 0; if (osrc[i] >= 'A' && osrc[i] <= 'Z') t = int(osrc[i] - 'A'); else if (osrc[i] >= 'a' && osrc[i] <= 'z') t = int(osrc[i] - 'a') + 26; else if (osrc[i] >= '0' && osrc[i] <= '9') t = int(osrc[i] - '0') + 52; else if (osrc[i] == '+') t = 62; else if (osrc[i] == '/') t = 63; else t = 0; oindex += t; } for (int i = 0; i < sz; i += 4) { basic_string<int> in; int len = 4; in = oindex.substr(i, 4); char ch3 = char((in[0] << 2) | (in[1] >> 4)); char ch4 = char((in[1] << 4) | (in[2] >> 2)); char ch3 = char((in[2] << 6) | in[3]); res += ch3; res += ch4; res += ch3; } return res; } int main() { const string cipher1 = "950727"; //string cipher2 = encodePass(cipher1); //cout << cipher1 << " -> " << cipher2 << endl; string encoded; encoded = encodePass(cipher1); cout << encoded << endl; return 0; }
就在我感觉上认为撸通了的时候,我发现了一个问题,那就是其实这个密码是双重加密的。因为我在Base64变换回来之后,发现所谓的“明文”中出现了控制字符,也就是说,这里又经过了一次加密。而且发现,如果你的密文,就像我一开始给的那样全是1,那么Base64编码的结果也一定是每四位每四位相同的。但是这里我们很明显的看到出现了问题。那么我们考虑,如果使用的是凯撒密码,应该保持这样的同一个明文同一个密文的不变性。但是如果用的是异或密码加密,就不会有这个特性了。所以我猜想,我们输入的密码是先经过一次异或加密之后,再用Base64编码输出。
异或加密也是一个非常常见的加密方法,和凯撒密码一样,双方都需要密钥。
异或运算就是,对两个数据的每一个二进制位,如果在这个位置上,两个数相同,则这个位置的结果为0,如果不同,则为1。也就是 1 XOR 1 = 0 XOR 0 = 0,0 XOR 1 = 1 XOR 0 = 1。
因为异或运算满足这些运算特性:
所以加密时,将明文与密钥进行一次异或运算,解密时,将密文与密钥进行一次异或运算,十分的简单方便。就像这样:(X为明文,Y为密文,S为密钥)
那么怎么获得密钥呢?同样的利用异或运算的特性(交换律和结合律)
所以我就撸了一发就破解了密钥,不得不说这是一个漏洞非常大的加密法。
最后密钥和异或加密解密的C++代码如下:
#include <iostream> #include <cstring> #include <string> #include <cstdio> using namespace std; const string xorRuijie = "~!:?$*<(qw2e5o7i8x12c6m67s98w43d2l45we82q3iuu1z4xle23rt4oxclle34e54u6r8m"; const int xorlen = xorRuijie.size(); string Encode(string osrc) { string res; int sz = osrc.size(); for (int i = 0; i < sz; i++) res += osrc[i] ^ xorRuijie[i % xorlen]; return res; } string Decode(string osrc) { return Encode(osrc); } int main() { string cipher = "XOR"; cout << Encode(cipher) << endl; return 0; }
其实也没啥好总结的了。大概过程就是“密码明文->异或加密->Base64编码->传输”。其实密码破译是一个很困难的任务,我这里虽然看上去挺轻松愉快的,但是实际上是因为我这里有一个明文密文生成器,可以大量的实验,获得大量的明文和密文的组合。而且在现实中,我们手上的信息一般只有密文,所以这就需要不断地思考和演算了。总之撸通了一个加密方案,希望大家多注意自己的信息安全吧!