记录下写加密文件脚本时候查阅的相关加密算法总结
本文涉及的加密算法
对称加密算法
DES, AES
密码模式
密码本模式
密码块链模式
密码反馈模式
对称加密算法
使用相同的密钥来加密和解密的算法成为对称加密算法
1.DES
DES计算方法如下图。
每次计算都是讲64bit明文拆分为左半部和右半部。下一轮的迭代输入左侧为上一轮的右半部,下一轮的右半部通过计算 $$ L_{i-1}\oplus f(R_{i-1}, K_i) $$ 其中 $K_i$ 表示当前所使用的密钥,而 $f(R_{i-1}, K_i)$ 的函数表示的是以下步骤
根据一个固定的替代和复制规则,将32位的 $R_{i-1}$扩展成一个一个48位的数字$E$
$E$和$K_i$被异或在一起
异或的结果被分成8个6位组,每个6位组被输入到不同S盒(置换)中。对于每个S盒共有$2^6$种可能输入,每种输入被映射到4位输出上。最后将这$8 X 4$位通过一个P盒(排列)
DES 附加材料wiki
例如映射出的4位的S盒的S5表格。S盒wiki
例如输入 0 1101 1那么寻找行01列1101的部分即为1001
S5 | Middle 4 bits of input | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 | ||
Outer bits | 00 | 0010 | 1100 | 0100 | 0001 | 0111 | 1010 | 1011 | 0110 | 1000 | 0101 | 0011 | 1111 | 1101 | 0000 | 1110 | 1001 |
01 | 1110 | 1011 | 0010 | 1100 | 0100 | 0111 | 1101 | 0001 | 0101 | 0000 | 1111 | 1010 | 0011 | 1001 | 1000 | 0110 | |
10 | 0100 | 0010 | 0001 | 1011 | 1010 | 1101 | 0111 | 1000 | 1111 | 1001 | 1100 | 0101 | 0110 | 0011 | 0000 | 1110 | |
11 | 1011 | 1000 | 1100 | 0111 | 0001 | 1110 | 0010 | 1101 | 0110 | 1111 | 0000 | 1001 | 1010 | 0100 | 0101 | 0011 |
其中16次迭代中使用了不同的密钥。算法开始前会对56位的密钥执行56位替换操作,每次迭代开始前也会将密钥分成两个28位,每部分向左循环移位,移位位数取决于当前迭代次数,移位之后,执行56位替代。
其中S盒和P盒操作代表的是密码置换和替换。
2.AES
随着DES安全性下降,慢慢被淘汰。
SSL 1.2版本以及TLS中已经使用了更加安全的AES算法来加密。
AES加密相对DES更加复杂,其过程是在一个4X4的字节矩阵中完成的,这个矩阵称为state。
算法的过程,参考《计算机网络》第八章的代码结构
#define LENGTH 16
#define NROWS 4
#define NCOLS 4
#define ROUNDS 10
typedef unsigned char byte;
rijndeal(byte plaintext[LENGTH], byte ciphertext[LENGTH], byte key[LENGTH]) {
int r; /*记录当前处理到第几轮*/
byte state[NROWS][NCOLS]; /*主要操作的矩阵*/
struct {
byte k[NROWS][NCOLS];
} rk[ROUNDS+1];
expand_keys(key, rk);
/*算法开始前会进行扩展key,扩展结果放在11个rk数组中。其中一个会被用在计算最开始处,其余被用在10轮计算中*/
xor_roundkey_into_state(state, rk[0]);
/*算法开始前,rk[0]和state先执行一个异或操作,然后存入state中*/
for(r=1; r<= ROUNDS; r++) {
substitute(state);
/*替代过程,这其中会有个一S盒,每个字节都会有对应得一个字节输出。
不同于DES存在多个S盒,这个过程仅有一个S盒*/
rotate_rows(state);
/*左循环移动,0行不移动,1行移动一位,2行移动两位,3行移动三位*/
if (r
Rijndael的加密结构即以上的部分。上面的加密过程是对于128位密钥的情况。输入的明文是16字节的,密钥也是16字节的。Rijndael的加密支持128,192,256位的密钥。
密码模式
注:以下公式中$P$代表明文(plaintext) $E$代表加密,$D$代表解密,$C$代表密文(cipher)
1.密码本模式
比如使用DES加密一段文本,如果使用密码本模式(ECB),则将明文分割成连续8字节的数据段来加密。这种模式成为密码本模式。这种模式带来的问题也显而易见,同样一份明文经过同一个密钥加密出来的结果是一样的,这样我们可以替换一个片段来达到不可告人的目的。《计算机网络》中举的例子很有意思,如果某个雇员知道公司年终奖文件的格式,比如说每个雇员的记录为32字节长的记录,16字节名字,8字节职位,8字节奖金,那么这个雇员可以通过替换记录的低8字节来达到替换奖金的目的,而且很有可能不被检测出来!
2.密码块链模式(CBC)
为了对抗加密段的攻击,那么就需要将每段加密段联系起来,这就有了密码块链模式。这种模式需要一个初始向量IV(Initialization Vector)来执行异或操作。
其加密过程公式为$$C_i = E_k(P_i \oplus C_{i-1})$$
其中初始 $C_0 = IV$
以AES-128-CBC为例。对于每一个16字节明文,首先和16字节的向量执行异或操作,然后再传入加密模块加密,最终加密的结果继续用于下一个16字节加密前的异或。
其解密过程公式为$$P_i = D_k(C_i) \oplus C_{i-1}$$
其中初始 $C_0 = IV$
这样的加密方式,对于相同的明文块不会导致相同的密文块。这也是其安全的部分。
下面是我之前写的一段脚本,用到了就是OpenSSL的AES-256-CBC的算法
require 'openssl'
require 'digest/sha2'
require 'securerandom'
class OpenSLLAESDemo
ALG = 'AES-256-CBC'
def initialize
end
def self.encrypt (file, password=nil)
cipher = OpenSSL::Cipher.new(ALG)
cipher.encrypt
if password.nil?
key = cipher.random_key
else
key = Digest::SHA256.hexdigest(password)
end
iv = SecureRandom.hex(32)
cipher.key =key
cipher.iv = iv
File.new("#{file}.enc",File::CREAT)
File.open("#{file}.enc", 'wb') do |enc|
File.open(file, 'rb') do |f|
loop do
r = f.read(4096)
break unless r
temp = cipher.update(r)
enc << temp
end
end
temp = cipher.final
enc << temp
end
[key, iv]
end
def self.decrypt (key,iv,file)
cipher = OpenSSL::Cipher.new(ALG)
cipher.decrypt
cipher.key = key
cipher.iv = iv
File.new("#{file}.dec",File::CREAT)
File.open("#{file}.dec", 'wb') do |dec|
File.open("#{file}.enc", 'rb') do |enc|
loop do
r = enc.read(4096)
break unless r
temp = cipher.update(r)
dec << temp
end
end
temp = cipher.final
dec << temp
end
end
def self.hash(file)
Digest::SHA256.hexdigest(File.open(file, 'r'){|f| f.read})
end
end
file = 'this is my file path'
temp = OpenSLLAESDemo.encrypt(file, 'iampassword')
puts temp[0]
puts temp[1]
OpenSLLAESDemo.decrypt(temp[0], temp[1], file)
puts 'yes' if OpenSLLAESDemo.hash(file) ==OpenSLLAESDemo.hash("#{file}.dec")
3.密文反馈模式
CBC可以说是最常用的模式,还以AES-128来说,它必须等待64字节的数据块到达后才能开始加密,这对于流式文件可以说是很不友好了。那么就有了密文反馈模式,这种模式可以改进这一点
先看它的公式吧
其加密公式为$$C_i = E_k(C_{i-1})\oplus P_i$$
解密为 $$P_i =E_k(C_{i-1})\oplus C_i$$
其中 $C_0 = IV$
看到这里需要注意解密过程,解密过程不是执行解密模块,而是一直加密。由于在解密过程中生成$P_i$的时候与$C_i$做异或的字节和生成$P_i$的时候与$C_i$做异或的是同一个字节,就可以保证解密的正确性。
对比CBC模式,发现这样其实就不用$E_k$中就不用等待数据块的到来就可以执行。这还是最简单的,一般实现过程中,会使用移位寄存器,来保存多个加密过数据段。比如说下面这张图
因此其加密公式也就变为$$C_i = head(E_k(S_{i-1}),x)\oplus P_i$$
解密公式变为 $$P_i = head(E_K(S_{i-1}), x)\oplus C_i$$
其中$S_0 = IV$, $S_i = ((S_{i-1}< $S_i$的公式就是128位的移位寄存器移位结果了。果然看公式理解起来更快了。 这些对称加密算法的使用场景一般是哪些呢? 我觉得只要适合文件加密或者信息加密的都适合。但由于是对称加密算法,所以加密解密需要同一个密钥,因此密钥管理是个大麻烦。这时候公钥加密RSA就派上用途了,但是RSA计算比较耗时,所以非对称加密和对称加密结合起来就是一个很完美的解决方案了。通过RSA传递AES的密钥,之后的对话就可以用AES来加密解密了。 下面展示一个简易版的HTTPS 通过SSL建立子协议的过程 之后的过程就可以使用对称加密来进行传输数据了。 在做笔记看wiki的时候发现了个很有意思的词条,时序攻击。那再来说说时序攻击吧。 至于什么是时序攻击呢?举个匹配过程的例子吧 当验证密码的时候,需要将外界输入的密码和真实密码对比,如果我们在一遇到不同的字符就返回比对失败,那么对于不同的输入这个时间往往是不同的,根据返回结果的时间就可以大致猜测到哪一位是不同的。 例如下面的代码 如果第一位就返回和最后一位返回,这个计算的时间肯定是不一样的。通过比对这个时间就可以判断失败位置。这样就讲攻击难度降下去很多。 对于上面的例子,应对方案也很简单,如果发现字符匹配失败,不要急着返回结果,做个标记,全部匹配完毕再返回即可。 《计算机网络(第五版)》 https://zh.wikipedia.org/zh/%... https://zh.wikipedia.org/wiki...用途
最后
for (int i=0; i
参考