加密算法笔记

记录下写加密文件脚本时候查阅的相关加密算法总结

本文涉及的加密算法

对称加密算法

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

S5Middle 4 bits of input
0000000100100011010001010110011110001001101010111100110111101111
Outer bits000010110001000001011110101011011010000101001111111101000011101001
011110101100101100010001111101000101010000111110100011100110000110
100100001000011011101011010111100011111001110001010110001100001110
111011100011000111000111100010110101101111000010011010010001010011

其中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<ROUNDS) {
            mix_columns(state);
            /*混淆操作,这过程中包含了一个在有限域GF(Galois field)(2^8)上的乘法*/
            xor_roundkey_into_state(state, rk[r]);   
            /*异或密钥,供下一轮加密使用*/
        }
    }
    copy_state_to_ciphertext(state, ciphertext)
}

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}<<x) + C_i) mod 2^n$

$S_i$的公式就是128位的移位寄存器移位结果了。果然看公式理解起来更快了。

用途

这些对称加密算法的使用场景一般是哪些呢?

我觉得只要适合文件加密或者信息加密的都适合。但由于是对称加密算法,所以加密解密需要同一个密钥,因此密钥管理是个大麻烦。这时候公钥加密RSA就派上用途了,但是RSA计算比较耗时,所以非对称加密和对称加密结合起来就是一个很完美的解决方案了。通过RSA传递AES的密钥,之后的对话就可以用AES来加密解密了。

下面展示一个简易版的HTTPS 通过SSL建立子协议的过程

《加密算法笔记》

之后的过程就可以使用对称加密来进行传输数据了。

最后

在做笔记看wiki的时候发现了个很有意思的词条,时序攻击。那再来说说时序攻击吧。

至于什么是时序攻击呢?举个匹配过程的例子吧

当验证密码的时候,需要将外界输入的密码和真实密码对比,如果我们在一遇到不同的字符就返回比对失败,那么对于不同的输入这个时间往往是不同的,根据返回结果的时间就可以大致猜测到哪一位是不同的。

例如下面的代码

for (int i=0; i<length; i++) {
    if (unchecked[i] != password[i]) return i;
}

如果第一位就返回和最后一位返回,这个计算的时间肯定是不一样的。通过比对这个时间就可以判断失败位置。这样就讲攻击难度降下去很多。

对于上面的例子,应对方案也很简单,如果发现字符匹配失败,不要急着返回结果,做个标记,全部匹配完毕再返回即可。

参考

    原文作者:加密算法
    原文地址: https://segmentfault.com/a/1190000008663835
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞