升序算法
- base64编码
tea加密算法
void Encrypt(long* EntryData, long* Key) {
//分别加密数组中的前四个字节与后4个字节,4个字节为一组每次加密两组
unsigned long x = EntryData[0];
unsigned long y = EntryData[1];
unsigned long sum = 0;
unsigned long delta = 0x9E3779B9;
//总共加密32轮
for (int i = 0; i < 32; i++) {
sum += delta;
x += ((y << 4) + Key[0]) ^ (y + sum) ^ ((y >> 5) + Key[1]);
y += ((x << 4) + Key[2]) ^ (x + sum) ^ ((x >> 5) + Key[3]);
}
//最后加密的结果重新写入到数组中
EntryData[0] = x;
EntryData[1] = y;
}
看代码,就是将[0],[1]分别赋值给x,y
遍历循环,通过每一次的sum的值不断的变换,进行一次又一次的加密
正向来看
(1)先进行sum的增加
(2)进行x的加密(通过异或的手段)
(3)进行相同的y加密(主要的是这里的y加密,加密过程中其实是有加密之后x的参与的)
(4)进行 [0][1]进行重新赋值
注意这里是一次循环的过程,实际上是这样直接循环加密32次
所以我们最后得到的,[0],[1]是之前加密之后的x,y赋值过来的
所以我们要直接逆向
void Decrypt(long* EntryData, long* Key) {
//分别加密数组中的前四个字节与后4个字节,4个字节为一组每次加密两组
unsigned long x = EntryData[0];
unsigned long y = EntryData[1];
unsigned long sum = 0;
unsigned long delta = 0x9E3779B9;
sum = delta << 5; //注意这里,sum = 32轮之后的黄金分割值. 因为我们要反序解密.
//总共加密32轮 那么反序也解密32轮
for (int i = 0; i < 32; i++) {
// 先将y解开 然后参与运算在解x
y -= ((x << 4) + Key[2]) ^ (x + sum) ^ ((x >> 5) + Key[3]);
x -= ((y << 4) + Key[0]) ^ (y + sum) ^ ((y >> 5) + Key[1]);
sum -= delta;
}
//最后加密的结果重新写入到数组中
EntryData[0] = x;
EntryData[1] = y;
可以看看这里的解密过程
首先将sum = delta<<5(直接将delta*32的值赋值给sum)
- 因为我们得到最终的答案是之前我们异或32次(也就是sum+=delat 32次之后的结果)
所以我们要逆向就要从delta*32的sum开始 - 为什么要先算y?
因为在最后得到的数据是先进行的x加密之后y加密
我们得到最终数据的y是由加密之后的x加密得到的
也就是说,我们只能先得到y,之后才能再得到x - 从后往前去,sum也要每一次都减少
所以sum-=delta
然后进行赋值操作
Xtea
加密前原始数据:1 2
加密后的数据:1347371722 925494771
解密后的数据:1 2
Process returned 0 (0x0) execution time : 0.020 s
Press any key to continue.
XTEA是TEA的升级版,增加了更多的密钥表,移位和异或操作等等,设计者是Roger Needham, David Wheeler
加密过程:
算法实现:
示例代码:
[cpp] view plain copy
include <stdio.h> #include <stdint.h> /* take 64 bits of data in v[0] and v[1] and 128 bits of key[0] - key[3] /
void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
for (i=0; i < num_rounds; i++) {
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
sum += delta;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
}
v[0]=v0; v[1]=v1;
}
void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=deltanum_rounds;
for (i=0; i < num_rounds; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}
v[0]=v0; v[1]=v1;
}
int main()
{
uint32_t v[2]={1,2};
uint32_t const k[4]={2,2,3,4};
unsigned int r=32;//num_rounds建议取值为32 // v为要加密的数据是两个32位无符号整数 // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位 printf("加密前原始数据:%u %u\n",v[0],v[1]);
encipher(r, v, k);
printf("加密后的数据:%u %u\n",v[0],v[1]);
decipher(r, v, k);
printf("解密后的数据:%u %u\n",v[0],v[1]);
return 0;
}
tea算法和xtea算法的区别
在于是对于数据的加密过程,tea是 ((y << 4) + Key[0]) ^ (y + sum) ^ ((y >> 5) + Key[1]);
而xtea是(((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);其中的差别在于tea中的key[i]是固定的,且对于数据进行了左移4的操作,而xtea中的key值是将下标进行了与(&)3的操作,所以下标会一组锁定在0,1,2,3的值之间,加的过程也是不一样的
tea和xtea的算法变种
-
我们可能在tea算法之中对于delta数据的值进行修改,在原本的值是0x9E3779B9,我们将其值进行修改
-
其次就是在我们的运算过程中添加可逆操作(比如异或),比如
x += ((y << 4) + Key[0]) ^ (y + sum) ^ ((y >> 5) + Key[1])^(sum+i)
y += ((x << 4) + Key[2]) ^ (x + sum) ^ ((x >> 5) + Key[3])^(sum+i)
在每一轮的加密过程中添加异或操作或者在运算的结果中添加异或操作
for (i=0; i < num_rounds; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}
v[0]=v0^0x??; v[1]=v1^0x??;
将结果进行异或操作
RSA算法
(1)乙方生成两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。
(2)甲方获取乙方的公钥,然后用它对信息加密。
(3)乙方得到加密后的信息,用私钥解密。 -
互质:
有两个正整数,除了一以外没有其他公因子,这两个数互质 1. 任意两个质数构成互质关系,比如13和61。 -
一个数是质数,另一个数只要不是前者的倍数,两者就构成互质关系,比如3和10。
-
如果两个数之中,较大的那个数是质数,则两者构成互质关系,比如97和57。
-
1和任意一个自然数是都是互质关系,比如1和99。
-
p是大于1的整数,则p和p-1构成互质关系,比如57和56。
-
p是大于1的奇数,则p和p-2构成互质关系,比如17和15。
-
欧拉函数:
第一种情况
如果n=1,则 φ(1) = 1 。因为1与任何数(包括自身)都构成互质关系。
第二种情况
如果n是质数,则 φ(n)=n-1 。因为质数与小于它的每一个数,都构成互质关系。比如5与1、2、3、4都构成互质关系。
第三种情况
如果n是质数的某一个次方,即 n = p^k (p为质数,k为大于等于1的整数)
则比如 φ(8) = φ(2^3) =2^3 - 2^2 = 8 -4 = 4。
第四种情况
如果n可以分解成两个互质的整数之积, n = p1 × p2
则 φ(n) = φ(p1p2) = φ(p1)φ(p2) -
模反元素
如果两个正整数a和n互质,那么一定可以找到整数b,使得 ab-1 被n整除,或者说ab被n除的余数是1。
这时,b就叫做a的模反元素
比如 5和7互质
a=5,n=7,(ab-1)%n=0
(5b-1)%7==0 b=3(多种可能)
过程: -
选取两个互质的正整数
比如 61 和53 (这两个数正好都是素数) -
所以根据欧拉函数可以得到的是素数的欧拉函数的值是本身-1
所以 n=61*63=3233
n的长度就是密钥长度。3233写成二进制是110010100001,一共有12位,所以这个密钥就是12位。实际应用中,RSA密钥一般是1024位,重要场合则为2048位。 -
φ(n) = (p-1)(q-1)=60*52=3120
-
随机选择一个整数e,条件是1< e < φ(n),且e与φ(n) 互质 1到3120之间,随机选择了17 所以e=17
-
总结上述的所得值: p=61 q=53 e=17 φ(n)=3120 d=???
根据模反元素,两个互质的数 e,n 一定可以找到一个数字d,使得(e*d-1)被φ(n)整除
ed - 1 = kφ(n) -
将n和e封装成公钥,n和d封装成私钥
公钥:(n,e)=(3233,17)
私钥:(n,d)=(3233,2753)
首先是甲加密需要用到公钥(n,e)
假如发送的信息是m=65(m必须是整数,可以对应ASCII或者其他,且小于n)
由公钥带去求得C值
C=2790,作为数据传输给乙
乙获取了C值之后需要运用自己的私钥(3233,2753)进行解密
得到结果m=65
Base64加密算法
https://cloud.tencent.com/developer/article/1868972
base64正常算法过程:
将要进行base64加密的每一个数据用8bit的二进制进行排序
这里我们以三个数据进行base64加密(77,97,110)
-
将三个数据的二进制数进行排序,因为一个数据的二进制数是8位,所以三位数的二进制数为24
-
我们将本来以8bit为一组的数据变成以6bit的四个数据
-
在上面每一组前面补两个0,扩展成32个二进制位,
此时变为四个字节:00010011、00010110、00000101、00101110。十进制:19、22、5、46 -
我们用base64编码表进行表示
-
所以 M , a , n (经过base64编码)——> T , W , F , t
这是正好以8,6公倍数进行之后的结果,假如不是8,6的公倍数:
我们也采用24位一组,但是每6bit不足的以0来补足,假如全是0则对应base64编码表的=
列子:
魔改的base64:
比如将base64编码表的次序进行打乱之类的
c语言相关base64:
RC4加密算法
RC4是属于对称密码算法中的流密码加密算法算法
对称密码:
加密和解密过程中使用的是用一个密钥,也就是说
我们通过
明文^密钥密文
密文^密钥明文
明文是公共给出的一个数据
通过密钥可以实现密文的获取
流密码
逐字节一个字节一个字节进行加密
- 初始S表(一般而言是256个)
对于S表的初始化,我们采用 S[256] (0-255) - 填充k表
我们利用一个种子密钥进行K表(256字节)的填充,假如种子密钥的值满足正好和256个字节,就可以直接完全填充,不满足则循环填充K表至满
比如 种子密钥是长度不为256字节的数据: 5,6,7
则我们的K表的初始化就是:5,6,7,5,6,7,5,6,7.........(至256字节) - 置换加密S表
通过我们的K表来进行S表的置换
我们通过公式进行置换
for (i = 0; i < 256; ++i)
{
j = (j + S[i] + K[i]) % 256;(取余值和S的大小)
ch_tmp = S[i]; // swap(S[i],S[j])
S[i] = S[j];
S[j] = ch_tmp;
} - 密钥流的生成(通过S表来获取密钥的值,异或得到密文)
初始化( i,j==0)
通过公式得到 i,j
for i in range(len(明文))
i = (i + 1) % 256
j = (j + S[i]) % 256
再进行swap( S[i] , S[j] )对于S表进行再一次的置换
获取 t 值
t=(S[i] + S[j]) % 256
k[r]=S[t] (获取密钥)
c语言实现:
include<stdio.h>
/*
RC4初始化函数
/
void rc4_init(unsigned char s, unsigned char* key, unsigned long Len_k)
{
int i = 0, j = 0;
char k[256] = { 0 };
unsigned char tmp = 0;
for (i = 0; i < 256; i++) {
s[i] = i; 初始化s表和k表
k[i] = key[i % Len_k];
}
for (i = 0; i < 256; i++) {
j = (j + s[i] + k[i]) % 256;
tmp = s[i]; 通过s表和k表一系列运算得到j值,作为下标进行s表的加密
s[i] = s[j];
s[j] = tmp;
}
}
/*
RC4加解密函数
unsigned char* Data 加解密的数据
unsigned long Len_D 加解密数据的长度
unsigned char* key 密钥
unsigned long Len_k 密钥长度
/
void rc4_crypt(unsigned char Data, unsigned long Len_D, unsigned char* key, unsigned long Len_k) //加解密
{
unsigned char s[256];
rc4_init(s, key, Len_k);
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k < Len_D; k++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256; 再进行一次置换
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256; s[t]为密钥
Data[k] = Data[k] ^ s[t];
}
}
void main()
{
//字符串密钥
unsigned char key[] = "zstuctf";
unsigned long key_len = sizeof(key) - 1;
//数组密钥
//unsigned char key[] = {};
//unsigned long key_len = sizeof(key);
//加解密数据
unsigned char data[] = { 0x7E, 0x6D, 0x55, 0xC0, 0x0C, 0xF0, 0xB4, 0xC7, 0xDC, 0x45,
0xCE, 0x15, 0xD1, 0xB5, 0x1E, 0x11, 0x14, 0xDF, 0x6E, 0x95,
0x77, 0x9A, 0x12, 0x99 };
//加解密
rc4_crypt(data, sizeof(data), key, key_len);
for (int i = 0; i < sizeof(data); i++)
{
printf("%c", data[i]);
}
printf("\n");
return;
}
//zstuctf{xXx_team_Is_GooD
python实现:(其中enc包含了url编码加密)
import urllib.parse
key="HereIsFlagggg"
enc = "%C2%A6n%C2%87Y%1Ag%3F%C2%A01.%C2%9C%C3%B7%C3%8A%02%C3%80%C2%92W%C3%8C%C3%BA"
enc = urllib.parse.unquote(enc)#URL编码加密
def init_sbox_kbox(key):
s_box = list(range(256))
j=0
for i in range(256):
j=(j+s_box[i]+ord(key[i%len(key)]))%256
s_box[i],s_box[j]=s_box[j],s_box[i]
return s_box
def enc_date(p,s_box):
result=[]
i,j=0,0
for s in p:
i=(i+1)%256
j=(j+s_box[i])%256
s_box[i],s_box[j]=s_box[j],s_box[i]
t=(s_box[i]+s_box[j])%256
k=s_box[t]#密钥
result.append(chr(ord(s)^k))#密文
return result
def rc4_(key,p):
s_box=init_sbox_kbox(key)
encode=enc_date(p,s_box)
for i in range(len(encode)):
print(encode[i],end='')
rc4_(key,enc)
RC4的算法变种
-
对于s表进行魔改,修改s表的值(原始的s表的数据是0-255的数值,比如将其修改为AES算法的S盒的初始值)
-
在运算过程中加入可逆运算,使得运算难度加大,比如:
tmp = s[i];
s[i] = s[j];
s[j] = tmp^0x?? 在第一次置换的过程中加入异或操作(同样在第二次置换的时候也可以这样)
for (k = 0; k < Len_D; k++)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] = Data[k] ^ s[t]^(k+0x??);异或上一个可逆的数值
}
AES加密算法
AES是对称加密算法,其加密和解密的过程使用的都是一个密钥。其次,AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。
在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。密钥的长度可以使用128位、192位或256位。密钥的长度不同,推荐加密轮数也不同,如下表所示:
我们在这里先总体来看看我们需要的加密过程:
在我们过程中的9轮加密中包括了:字节代换,行位移,列混合以及轮密钥加的四个过程
而在我们的最后一轮最终轮加密的过程中则只有:字节代换,行位移以及轮密钥加三个过程
AES加密过程:
加密过程一
初始变换(inital round)
这里的初始变换主要是用于将输入的明文矩阵数据进行异或一个密钥矩阵
加密过程二
字节代换
通过一个S盒对于初始化变化之后的数据进行一个字节代换
以下是一个16*16的矩阵就是AES加密的S盒了
0 1 2 3 4 5 6 7 8 9 A B C D E F
0 63 7c 77 7b f2 6b 6f c5 30 1 67 2b fe d7 ab 76
1 ca 82 c9 7d fa 59 47 f0 ad d4 a2 af 9c a4 72 c0
2 b7 fd 93 26 36 3f f7 cc 34 a5 e5 f1 71 d8 31 15
3 4 c7 23 c3 18 96 5 9a 7 12 80 e2 eb 27 b2 75
4 9 83 2c 1a 1b 6e 5a a0 52 3b d6 b3 29 e3 2f 84
5 53 d1 0 ed 20 fc b1 5b 6a cb be 39 4a 4c 58 cf
6 d0 ef aa fb 43 4d 33 85 45 f9 2 7f 50 3c 9f a8
7 51 a3 40 8f 92 9d 38 f5 bc b6 da 21 10 ff f3 d2
8 cd c 13 ec 5f 97 44 17 c4 a7 7e 3d 64 5d 19 73
9 60 81 4f dc 22 2a 90 88 46 ee b8 14 de 5e b db
a e0 32 3a a 49 6 24 5c c2 d3 ac 62 91 95 e4 79
b e7 c8 37 6d 8d d5 4e a9 6c 56 f4 ea 65 7a ae 8
c ba 78 25 2e 1c a6 b4 c6 e8 dd 74 1f 4b bd 8b 8a
d 70 3e b5 66 48 3 f6 e 61 35 57 b9 86 c1 1d 9e
e e1 f8 98 11 69 d9 8e 94 9b 1e 87 e9 ce 55 28 df
f 8c a1 89 d bf e6 42 68 41 99 2d f b0 54 bb 16
使用的过程就是:
将初始代换之后的矩阵的每个值进行一个代换
比如:矩阵中的第一个数据为:0x12
高位数据作为行,低位数据作为列(高行低列)
所以我们去对照第一行第二列的位置——>0xc9
所以就将0x12——>0xc9
逆S盒
0 1 2 3 4 5 6 7 8 9 A B C D E F
0 52 9 6a d5 30 36 a5 38 bf 40 a3 9e 81 f3 d7 fb
1 7c e3 39 82 9b 2f ff 87 34 8e 43 44 c4 de e9 cb
2 54 7b 94 32 a6 c2 23 3d ee 4c 95 b 42 fa c3 4e
3 8 2e a1 66 28 d9 24 b2 76 5b a2 49 6d 8b d1 25
4 72 f8 f6 64 86 68 98 16 d4 a4 5c cc 5d 65 b6 92
5 6c 70 48 50 fd ed b9 da 5e 15 46 57 a7 8d 9d 84
6 90 d8 ab 0 8c bc d3 a f7 e4 58 5 b8 b3 45 6
7 d0 2c 1e 8f ca 3f f 2 c1 af bd 3 1 13 8a 6b
8 3a 91 11 41 4f 67 dc ea 97 f2 cf ce f0 b4 e6 73
9 96 ac 74 22 e7 ad 35 85 e2 f9 37 e8 1c 75 df 6e
a 47 f1 1a 71 1d 29 c5 89 6f b7 62 e aa 18 be 1b
b fc 56 3e 4b c6 d2 79 20 9a db c0 fe 78 cd 5a f4
c 1f dd a8 33 88 7 c7 31 b1 12 10 59 27 80 ec 5f
d 60 51 7f a9 19 b5 4a d 2d e5 7a 9f 93 c9 9c ef
e a0 e0 3b 4d ae 2a f5 b0 c8 eb bb 3c 83 53 99 61
f 17 2b 4 7e ba 77 d6 26 e1 69 14 63 55 21 c 7d
逆S盒的作用就是将原本进行了字节代换的数据代换回去
比如刚刚我们将0x12——>0xc9 现在我们去找第c行第9列得到的值就是0x12
行变换
我们以4*4的矩阵来举例:
第一行变,第二行向左移动一位,第三行向左移动二位.......
列混合
我们同样以44的矩阵举例:
我们需要将行变换之后的矩阵左乘一个给定的44矩阵
左乘不是相乘
矩阵与矩阵的相乘是第一行和另一矩阵的第一列相乘相加的结果作为第一个数据
矩阵与矩阵的左乘是第一行和另一矩阵的第一列相乘相异或的结果作为第一个数据
左乘
其实是我们的左乘是有公式的比如这里我们要得到相左乘之后的s(0,0)的值
其实就是s'(0,0)=02s(0,0)03*s(1,0)01s(2,0)^01*s(3,0)
注意这里的相乘不是简单的相乘的结果:
这里就是一个公式了
首先我们要确定的是我们进行的实际上是位运算:
- 将我们要进行列混合的数据先看它的二进制位,假如是与2(二进制00000010)相乘,且假如二进制位的首位为1,则将数据左移一位,将a7去除,低位补零;假如二进制的首位为0,则将数据左移一位,a7去除,低位补零
- 假如相乘的值比2大(比如3,就将其乘以2的结果再异或上(本来是加上的,在左乘的算法中加变为异或)原值
轮密钥加
这是每轮最后的一次加密:将我们进行列混合的结果通过轮密钥矩阵异或得到最终的结果
这里我们首先要获取轮密钥矩阵:
我们的轮密钥矩阵是通过我们的密钥拓展得到的
密钥拓展
我们通过公式
我们通过原本的密钥进行正常排序,每一行开始设置从w0开始,所以w4之前的我们都是已知的
所以我们怎么去获取之后的w值??
我们的密钥拓展过程主要是通过看其下标的值是否被4整除
如果i不是4的倍数,那么第i列由如下等式确定:
W[i]=W[i-4]⨁W[i-1]
比如: W[5]=W[1]^W[4]
如果i是4的倍数,那么第i列由如下等式确定:
W[i]=W[i-4]⨁T(W[i-1])
比如: W[4]=W[0]^T(W[3])
这里的T是一个函数主要的函数组成就是:字循环、字节代换和轮常量异或
字循环:将数据进行一个左移1个字节的操作
比如要求W[4]=W[0]^T(W[3]),就先去求T(W[3]),如同上述图一样,这样的排列方式是因为按照矩阵的过程进行排列的,按照数值来看 {c9,cf,4f,3c}——>{cf,4f,3c,c9}
字节代换
同上面是字节代换一样,通过S盒的定位进行一个字节代换
轮常量异或
.轮常量异或:将前两步的结果同轮常量Rcon[j]进行异或,其中j表示轮数。
轮常量:
Rcon = {
1: ['0x01', '0x00', '0x00', '0x00'],
2: ['0x02', '0x00', '0x00', '0x00'],
3: ['0x04', '0x00', '0x00', '0x00'],
4: ['0x08', '0x00', '0x00', '0x00'],
5: ['0x10', '0x00', '0x00', '0x00'],
6: ['0x20', '0x00', '0x00', '0x00'],
7: ['0x40', '0x00', '0x00', '0x00'],
8: ['0x80', '0x00', '0x00', '0x00'],
9: ['0x1B', '0x00', '0x00', '0x00'],
10: ['0x36', '0x00', '0x00', '0x00']