区块链中的密码学系列之对称加密算法的分组模式(六)

  1. 前言

     众所周知,由于对称加密算法只能加密固定长度的明文。如果我们想加密任意长度的明文,则需要对明文进行分组,然后对每组进行加密。
    
    
    
     在密码学中,被称为分组加密(Block cipher)。将明文分成多个等长的模块,然后使用算法对每组进行加密。现代的分组加密的是创建在迭代的思想上的,这种思想来自

香农的《保密系统的通信理论》。哈哈,就是我们在本科学习到的那个大佬香农。值得注意的是,迭代产生的密文在每一轮中使用不同的子密钥,而这些子密钥由原密钥生成。

我们熟知的DES和AES都是分组密码实现的。
  1. 分组密码的系统模型

     分组密码实质上是字长为m的数字序列的代换密码。注意,在一般情况下,n和m是相等的。
  2. 分组密码的设计思想

扩散(diffusion):将明文冗余度分散到密文中。进行扩散最简单的方法是置换(Permutation),即重新排列字符。

混淆(confusion):让明文和密文之间的关系复杂化,这样做是为了防止通过统计分析进而破译密码学。常用的方法是代换(Substitution)。

在实际中,代换常用代换表,用查表法来实现。常用的基本代换有以下几种:

  1. 循环移位(Shift left/right circular)

2 .模2n加 (Addition with module)

  1. 线性变换(Linear transformation)

  2. 换位/置换(Transposition)
  3. 仿射变换(Affine transform)

  4. 分组密码的设计要求

    扫描二维码关注公众号,回复: 5615043 查看本文章

分组长度足够大(64~128~256比特)

密钥量要足够大(64~128~192~256比特)

算法足够复杂(包括子密钥产生算法)

加密、解密算法简单,易软、硬件实现

便于分析(破译是困难的,但算法却简洁清晰)

其中,比较经典的实现是Feistel网络。Feistel网络利用乘积密码实现关键密码模块。

关于Feistel的具体讲解参考:区块链中的密码学系列之对称加密算法DES(四)

  1. 分组密码运行模式

     前面我们知道,分组密码在加密时明文分组的长度是固定的,而实用中待加密消息的数据量是不定的,数据格式可能是多种多样的。所以我们需要进行分组。为了更好的隐蔽明文的统计特性、数据的格式等,以提高整体的安全性, 降低删除、重放、插入和伪造成功的机会。我们必须要选用适当的分组运行模式。
    
    
    
     常见的分组运行模式主要有四种:
    
    
    
     电码本 (ECB, electronic codebook);
    
     密码分组链接 (CBC, cipher block chaining);
    
     密码反馈 (CFB, cipher feedback);
    
     输出反馈 (OFB, output feedback)。

下面我们进行一一学习。

5.1 ECB模式

  ECB模式,全称Electronic Codebook模式,译为电子密码本模式,即用相同的密码分别对明文分组独立加密。

    ECB模式适用于短数据(如加密密钥)的加密。如:安全传递DES密钥。其缺点是相同的明文分组会加密为相同的密文分组,因此存在一定风险。

值得注意的是,如果最后一个分组的长度不够时,需要使用特殊的数据进行填充。

5.2 CBC模式

  CBC模式,全称Cipher Block Chaining模式,即密文分组链接模式,即加密算法的输入是上一个密文分组和下一个明文分组的异或。因为是将上一个密文分组和下一个明文分组的内容混合加密,因此可以避免ECB模式的缺陷。当加密第一个明文分组时,由于不存在上一个密文分组,因此需要准备与分组等长的初始化向量IV,来代替上一个密文分组。

值得注意的是,其缺点是比特的错误传播,将影响下一组的解密。

在Fabric的AES算法的实现中,采用的就是CBC模式,源码地址:

https://github.com/hyperledger/fabric/blob/release-1.4/bccsp/sw/aes.go

func aesCBCEncrypt(key, s []byte) ([]byte, error) {
return aesCBCEncryptWithRand(rand.Reader, key, s)
}

func aesCBCEncryptWithRand(prng io.Reader, key, s []byte) ([]byte, error) {
if len(s)%aes.BlockSize != 0 {
return nil, errors.New("Invalid plaintext. It must be a multiple of the block size")
}

block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

ciphertext := make([]byte, aes.BlockSize+len(s))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(prng, iv); err != nil {
return nil, err
}

mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], s)

return ciphertext, nil
}

func aesCBCEncryptWithIV(IV []byte, key, s []byte) ([]byte, error) {
if len(s)%aes.BlockSize != 0 {
return nil, errors.New("Invalid plaintext. It must be a multiple of the block size")
}

if len(IV) != aes.BlockSize {
return nil, errors.New("Invalid IV. It must have length the block size")
}

block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

ciphertext := make([]byte, aes.BlockSize+len(s))
copy(ciphertext[:aes.BlockSize], IV)

mode := cipher.NewCBCEncrypter(block, IV)
mode.CryptBlocks(ciphertext[aes.BlockSize:], s)

return ciphertext, nil
}

func aesCBCDecrypt(key, src []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

if len(src) < aes.BlockSize {
return nil, errors.New("Invalid ciphertext. It must be a multiple of the block size")
}
iv := src[:aes.BlockSize]
src = src[aes.BlockSize:]

if len(src)%aes.BlockSize != 0 {
return nil, errors.New("Invalid ciphertext. It must be a multiple of the block size")
}

mode := cipher.NewCBCDecrypter(block, iv)

mode.CryptBlocks(src, src)

return src, nil
}

// AESCBCPKCS7Encrypt combines CBC encryption and PKCS7 padding
func AESCBCPKCS7Encrypt(key, src []byte) ([]byte, error) {
// First pad
tmp := pkcs7Padding(src)

// Then encrypt
return aesCBCEncrypt(key, tmp)
}

// AESCBCPKCS7Encrypt combines CBC encryption and PKCS7 padding using as prng the passed to the function
func AESCBCPKCS7EncryptWithRand(prng io.Reader, key, src []byte) ([]byte, error) {
// First pad
tmp := pkcs7Padding(src)

// Then encrypt
return aesCBCEncryptWithRand(prng, key, tmp)
}

// AESCBCPKCS7Encrypt combines CBC encryption and PKCS7 padding, the IV used is the one passed to the function
func AESCBCPKCS7EncryptWithIV(IV []byte, key, src []byte) ([]byte, error) {
// First pad
tmp := pkcs7Padding(src)

// Then encrypt
return aesCBCEncryptWithIV(IV, key, tmp)
}

// AESCBCPKCS7Decrypt combines CBC decryption and PKCS7 unpadding
func AESCBCPKCS7Decrypt(key, src []byte) ([]byte, error) {
// First decrypt
pt, err := aesCBCDecrypt(key, src)
if err == nil {
return pkcs7UnPadding(pt)
}
return nil, err
}

5.3 CFB模式

  CFB模式,全称Cipher FeedBack模式,译为密文反馈模式,即上一个密文分组作为加密算法的输入,输出与明文异或作为下一个分组的密文。在CFB模式中,明文分组和密文分组之间只有一次异或。

5.4 OFB模式

 OFB模式,全称Output Feedback模式,译为输出反馈模式。OFB模式与CFB模式类似,只是加密算法的输入是上一次加密的输出。在OFB模式中,异或所需的密钥流,可以事先通过密码算法生成,即生成密钥流的操作可以与异或运算并行。

  OFB模式加密和处理解密逻辑相同,明文与密钥流异或生成密文,密文与密钥流异或生成明文。

5.5 分组密码运行模式比较

  1. 分组密码的分析

密码分析学中的假设:

​人们总是假定攻击者可以截获在不安全信道上所传输的所有密文。

​另一被广泛接受的假设是Kerchhoff假设:除密钥外,攻击者知道加密和解密的详细过程。

攻击分组密码常用的方法:

强力攻击:密钥穷尽搜索攻击。

​差分密码分析:通过逐轮分析明文对的差值对密文对差值的影响来恢复密钥或密钥的某些比特。

线性分析:基本思想是通过寻找一个给定密码算法有效的线性近似表达式来分析破译密码系统。

猜你喜欢

转载自www.cnblogs.com/anapodoton/p/10578224.html