对称加密的基本原理 —— 从DESede/CBC/PKCS5Padding说起

DESede/CBC/PKCS5Padding加密算法包含三个部分,对称加密方式为DESede,分组加密模式为CBC,填充算法是PKCS5Padding。本文从对称加密的基本原理来了解这个算法。

基本原理

混淆与扩散

混淆(confusion)与扩散(diffusion)是设计密码学算法的两种主要方法。最早出现在克劳德·香农1945年的论文《密码学的数学理论》当中。

  • 混淆(confusion)

混淆主要是用来使 密文 和对称式加密方法中 密钥 的关系变得尽可能的复杂;此属性使得很难从密文中找到密钥,并且如果更改了密钥中的单个位,将影响密文中大多数或所有位的值的计算。做到这一点最容易的方法是"代替",如DES中的S-替换。

  • 扩散(diffusion)

扩散的思想是隐藏密文和明文之间的关系,明文每一位影响密文多的位,明文中任何一点小更动都会使得密文有很大的差异,让明文和密文之间的尽量没有统计关系,从而使明文的统计特征在密文中消失,相当于明文的统计结构被扩散。产生扩散最简单的方法是"换位(置换)",如DES中的通过16轮的P-置换实现扩散

混淆与扩散的实现

通常通过可重复的一系列替换(Substitution)和置换(Permutation)来实现混淆和扩散。替换是指按照某些规则用其他组件替换某些组件(通常是位)。置换是指根据某种算法对位的顺序进行操作。

加密算法

DES(Data Encryption Standard)

DES最初出现在1970年代早期,美国政府用DES加密政府内非机密敏感信息。DES现在已经不是一种安全的加密方法,为了提供实用所需的安全性,可以使用DES的派生算法3DES来进行加密,虽然3DES也存在理论上的攻击方法,DES标准和3DES标准已逐渐被高级加密标准(AES)所取代。

特点:

  • 明文和密文为64位分组长度
  • 加密和解密除秘钥编排不同外,使用同一算法
  • 密钥长度:56位。但看起来64位,每个第8位为奇偶校验
  • 采用替代和置换的组合,共16轮
  • 只是用算术和逻辑运算,易于实现

子密钥的生成

子秘钥的生成

加密过程:

加密过程

Ln 和 Rn 公式

Ln = Rn-1  
Rn= Ln XOR F(Rn-1, Kn-1)  
  • 初始置换IP

初始置换和逆初始置换并没有增加DES的安全性

  • 第一轮加密 <一 key1


  • 第十六轮加密 <一key16

  • 逆初始置换IP-1

迭代过程F:

  • 密钥置换
    密钥:置换选择1 一> 循环左移 一> 置换选择2

  • 扩展置换
    数据:从32位扩展到48位

  • S-盒代替
    压缩替代S,扩展后48位数据和密钥进行异或运算,进行S盒代替,输出32位数据。S-盒是DES的核心,是该算法中唯一非线性元素,实现了局部混淆。

  • P-盒置换
    使S-盒输出对下一轮多个S盒产生影响,引起雪崩效应。

3DES(DESede)

3DES包含3个DES密钥,K1,K2和K3,均为56位(除去奇偶校验位)。

加密算法为:

密文 = EK3(DK2(EK1(明文)))

解密则为其反过程:

明文 = DK1(EK2(DK3(密文)))

即以K3解密,以K2加密,最后以K1解密。每次加密操作都只处理64位数据,称为一块。

分组加密模式

分组加密(Block cipher),又称分块加密或块密码,是一种对称密钥算法。它将明文分成多个等长的模块(block),使用确定的算法和对称密钥对每组分别加密解密。典型的如AES和3DES作为美国政府核定的标准加密算法,应用领域从电子邮件加密到银行交易转帐,非常广泛。

现代分组加密创建在迭代的思想上产生密文。其思想由克劳德·香农在他1949年的重要论文《保密系统的通信理论》中提出,作为一种通过简单操作如替代和排列等以有效改善保密性的方法。

迭代产生的密文在每一轮加密中使用不同的子密钥,而子密钥生成自原始密钥。

CBC

  • CBC加密模式
    CBC加密模式

  • CBC解密模式
    CBC解密模式

  • CBC加密过程

CBC模式加密过程

  • CBC解密过程

解密过程

CBC是最为常用的工作模式。它的主要缺点在于加密过程是串行的,无法被并行化,而且消息必须被填充到块大小的整数倍。

填充算法

分组加密算法中,最后一个块的长度是不固定的,未必刚好等于块的大小,所以要标识最后一块的大小,以及填充了多少字符。

  • ZeroPadding

数据长度不对齐时使用0填充,否则不填充。使用0填充有个缺点,当元数据尾部也存在0时,在unpadding时可能会存在问题。

  • PKCS5Padding

假设每个区块大小为blockSize

a. 已对齐,填充一个长度为blockSize且每个字节均为blockSize的数据。

b. 未对齐,需要补充的字节个数为n,则填充一个长度为n且每个字节均为n的数据。

  • PKCS7Padding

和pkcs5不同的是,要求块的大小为8.

由于使用PKCS7Padding/PKCS5Padding填充时,最后一个字节肯定为填充数据的长度,所以在解密后可以准确删除填充的数据,而使用ZeroPadding填充时,没办法区分真实数据与填充数据,所以只适合以\0结尾的字符串加解密。

go实现DESede/CBC/PKCS5Padding

package main

import (
	"bytes"
	"crypto/cipher"
	"crypto/des"
	"encoding/base64"
	"errors"
	"fmt"
)

var (
	PKCS5 = &pkcs5{}
)

type pkcs5 struct{}

func (p *pkcs5) Padding(src []byte, blockSize int) []byte {
	srcLen := len(src)
	padLen := blockSize - (srcLen % blockSize)
	padText := bytes.Repeat([]byte{byte(padLen)}, padLen)
	return append(src, padText...)
}

func (p *pkcs5) Unpadding(src []byte, blockSize int) ([]byte, error) {
	srcLen := len(src)
	paddingLen := int(src[srcLen-1])
	if paddingLen >= srcLen || paddingLen > blockSize {
		return nil, errors.New("padding size error")
	}
	return src[:srcLen-paddingLen], nil
}

func Encrypt(encryptor cipher.BlockMode, msg []byte) (cipherText []byte, err error) {
	if msg != nil {
		msg = PKCS5.Padding(msg, encryptor.BlockSize())
		if len(msg) < encryptor.BlockSize() || len(msg)%encryptor.BlockSize() != 0 {
			return nil, errors.New("msg length error")
		}
		cipherText = msg
		encryptor.CryptBlocks(cipherText, msg)
	}
	return
}

func Decrypt(decryptor cipher.BlockMode, cipherText []byte) (msg []byte, err error) {
	if decryptor != nil {
		if len(cipherText) < decryptor.BlockSize() || len(cipherText)%decryptor.BlockSize() != 0 {
			return nil, errors.New("cipherText length error")
		}
		msg = cipherText
		decryptor.CryptBlocks(msg, cipherText)
		msg, err = PKCS5.Unpadding(msg, decryptor.BlockSize())
	}
	return
}

func DESedeCBCPKCS5Decrypt(cipherText string, secret string) (string, error) {
	if len(secret) != 24 {
		return "", errors.New("secret length != 24 ")
	}

	var block cipher.Block
	var err error
	var dbm cipher.BlockMode

	key := []byte(secret)

	if block, err = des.NewTripleDESCipher(key); err != nil {
		return "", err
	}

	//CBC分组加密模式的初始化向量组,这里取key的前8位
	iv := key[0:8]
	dbm = cipher.NewCBCDecrypter(block, iv)

	base64Decoded, err := base64.StdEncoding.DecodeString(cipherText)
	if err != nil {
		return "", err
	}

	decryptedData, err := Decrypt(dbm, base64Decoded)
	if err != nil {
		return "", err
	}

	return string(decryptedData), nil
}

func DESedeCBCPKCS5Encrypt(msg string, secret string) (string, error) {
	if len(secret) != 24 {
		return "", errors.New("secret length != 24 ")
	}

	var block cipher.Block
	var encryptedData []byte
	var err error
	var ebm cipher.BlockMode

	key := []byte(secret)
	if block, err = des.NewTripleDESCipher(key); err != nil {
		return "", err
	}

	iv := key[0:8]
	ebm = cipher.NewCBCEncrypter(block, iv)

	if encryptedData, err = Encrypt(ebm, []byte(msg)); err != nil {
		return "", err
	}

	return base64.StdEncoding.EncodeToString(encryptedData), nil
}

func main() {
	secret := "123456781234567812345678"
	cipherText := `N8J1r5Eh4VbqiuCt+XtTzw==`
	rawMsg := "helloworld"

	decryText, err := DESedeCBCPKCS5Decrypt(cipherText, secret[0:24])
	fmt.Println(decryText, err)

	encryptText, err := DESedeCBCPKCS5Encrypt(rawMsg, secret[0:24])
	fmt.Println(encryptText, err)
}


参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值