说明:本文仅用于个人总结
总括:本文将涵盖的加密方式有:md5,rabbit,RSA算法,和番外篇——个人近期在计算机组成原理学习的总结。
1.rabbit
密文特征:以“U2FsdGVkX1”开头的,只含有26个大小写英文字母和符号'='.'+'.'/'。
原理:在网上翻找不到什么资料,仅仅知道这是一种高速流密码,算法核心组件是一个位流生成器,每次迭代加密128个消息位。
2.md5
密文特征:长度始终为16位
原理:一种哈希算法,比较复杂,由于时间关系后续补上。
曾被视为不可逆过程,但是被我国王小云院士所带领的团队破解(碰撞攻击),但是目前仍然未被完全抛弃
之所以认为不可逆,在于加密过程造成了原始信息的损失(反复异或运算、移位运算)
3. RSA算法
密文特征:略,密钥很长
加密原理:
——前置知识:欧拉函数,扩展欧几里得算法,费马定理——
简单的来讲:
其中C表示密文,M表示明文,e和n表示公钥
而解密的过程也可以用一个简单的公式表示:
其中的d和n表示私钥
接下来是具体的原理过程:
首先需要俩个不等的质数p,q。
/*在日常的使用中,p,q会取得很大,作为举例,我们取p 、q分别为3,11*/
接着计算出n,n=p*q
/*这一段过程就是RSA安全性的主要原理,因为这个过程中得到n很容易,但是要得到p和q就很难了,在p和q都是很大的数字的情况下这个过程以目前的技术理论上约等于不可逆,在我们的例子中,n=33*/
计算n的欧拉函数:
/*数学上,如果n可以分解为2个互质的整数之积,那么n的欧拉函数等于这俩个因子的欧拉函数之积。
欧拉函数:小于n的正整数中与n互质的数的数目,故如果n是质数,那么
在我们的例子中,这里的值就是20*/
选择一个与互质的整数e,计算出e对于
模反元素d:
/*数学概念补充:如果俩个正整数a,b互质,那么一定可以找到一个整数d,使得a*d-1被b整除,即模b的值为1,此时,d就叫a的模反元素
在我们的例子中,这个e可以取3,此时的d就为7.
至此我们就得到了所有的密钥,那么如果我们要加密20,就可以得到20的密文为20的3次方模33,即14,反过来,14的7次方模33,即为20
有意思的是,在我举例的过程中出现了多次密文等于明文的现象,也可能是算错了,但其中也许还蕴含着什么其他的性质*/
在实际解题的过程中并不是n、p、q、e、d都清楚的,往往是缺少条件让我们利用现有条件求解其中的任一值,如在BUUCTF测评中的一道RSA题目是这样的:
这就需要我们自行编写python脚本求解,虽然有时有些工具能帮我们解决一部分问题
不过上述题目并不复杂,只需要使用工具RSA tool即可解决
下面是一段用于解决其他情况下问题的代码示例:
import libnum
n = 7294497858957901445355947735166842484749096814665022387008217401257019413756727238563729984979351091643875115017365550264387329725016456965013930271820047
q = 88477499437838960341976664020853034752908783275823255928543934186049033583799
c = 3311225443579004821241076827926847285313653720940847155804689699917553392274178773122692141011939070001554660943147119803678623000461344775911575948301101
e = 65537
p = n//q
print(p)
d = libnum.invmod(e,(p-1)*(q-1))
print(d)
i = pow(c,d,n)
print(libnum.n2s(i))
这段代码的运行结果如下:
(d太长了截不下)
代码中的libnum库可以通过windows的cmd命令行下载:pip install libnum
如果提示需要更新按照给出的代码输入即可,若发生一些报错可以尝试重复执行一次更新代码
后续也许会专门写个合集整理各种RSA的情况
番外篇——计算机组成原理学习总结
总的来说本周学习不多,首先摆上一张基础的图:
声明:图片来源bilibili用户:码百万 的计算机底层逻辑视频第一集
在我们运行磁盘内的程序时,操作系统首先会将程序load入内存中形成进程,分配对应资源(如存储空间、网络接口)。一个程序可以形成多个进程。
操作系统寻得程序中第一句代码(main方法),再把指令或数据传输给CPU,CPU进行需要的数据处理,再将处理完成的数据传输回程序,直到最后一句代码结束。这个动态过程为线程
可以看到,进程是静态的,线程是动态的
CPU的多线程:
多个线程的快速切换,A线程进行一段时间后停止,再进行B线程的一部分
也就是在一个时间片中,1个CPU仍然只能处理一个线程
线程的切换也需要时间,在某些情况下多线程状态也可能比单线程耗时长
缓存&缓存行:
CPU和内存的速度比较:CPU访问本地寄存器(Registers)和基本计算单元(ALU)时间与访问内存的速度大约为100:1
故而引入缓存处理实现速度对等的情况,减少了时间浪费。
缓存层数的选择:目前最流行的为三级缓存,是平衡时间与空间的妥协
如图:
图源同上
缓存的基本单元:缓存行,多数为64字节为一个单位
缓存一致性:各个缓存层中同时存在的缓存行需要保持一致性,其中一个发生了数据更改则其它缓存层中的缓存行也需要同时更改,这个过程需要时间,不同CPU有不同的机制维持缓存一致性。
故而,如果可以避免缓存一致性的维护,则在一定程度上可以优化部分代码。不过代价则是付出更多的空间,以下代码为例:
import java.util.*
public class text{
public static long COUNT = 1000000000;
private static class T{
private long p1,p2,p3,p4,p5,p6,p7;
public long x=0L;
private long p9,p11,p12,p13,p14,p15;
}
public static T[] orr=new T[2];
static{
arr[0] = new T();
arr[1] = new T();
}
public static void main(String[] args) throws Exception{
CountDownLatch latch = new CountDownLatch(2);
Thread t1 = new Thread(() ->{
for(long i = 0; i < COUNT;i++)
arr[0].x = i;
latch.countDown();
});
Thread t2 = new Thread(() ->{
for(long i = 0; i < COUNT;i++)
arr[0].x = i;
latch.countDown();
});
final long start = System.nonoTime();
t1.start;
t2.start;
latch.await();
System.out.println((System.nonoTime() - start) / 100_0000);
}
}
如果将上述代码中的p1,p2...定义段和p7,p8...定义段去除(注释掉),前后运行对比结果发现注释前的代码运行时间明显短于注释后的代码(大约为241:762)
原理就在于通过定义空的p1-p15创造空间,完成了arr[0]和arr[1]的缓存行隔离,进而避免了缓存一致性的维护,从而在十万次的更改数据操作中体现了时间的缩短。
这个方法也叫做缓存行对齐
在实际工程中,这个方法也有所运用
如在开源领域中的著名框架Disruptor(单机最快的MQ)中采用了这样的方法:
Disruptor:使用环形缓存区(数组形式存储)(RingBuffer类),从而实现更改传统双指针法,改用单指针读取存放。在多线程同时访问这一个指针时,就使用了这样的优化方法
其中p1-p7在RingBuffer类中声明,在其父类(RingBuffFields)的父类(RingBufferPad)中还有p1-p7的声明。
写到这里已经很晚了,下周的事下周再说,本篇到此为止