对称密码及案例

1. 概述

1.1 基本概念

根据密钥的使用方式,可以将密码分为对称密码和非对称密码(公钥密码)。

对称密码(symmetric cryptography)是指在加密和解密时使用同一密钥的方式。而公钥密码(public key cryptography)则是在加密和解密时使用不同的密钥。

常用的对称密码有:DES、3DES、AES等等。

1.2 特性

  • 加密和解密使用同一个密钥
  • 加密和解密速度较快,适合加密比较大的数据
  • 密钥传递的过程不安全,而且容易被破解,密钥管理也比较麻烦

2. DES

2.1 介绍

DES(Data Encryption Standard)是1977年美国联邦信息处理标准(FIPS)中所采用的一种对称密码(FIPS46.3)。DES一直以来被美国以及其他国家的政府和银行等广泛使用。然而,随着计算机的进步,现在DES已经能够被暴力破解,强度大不如从前。

2.2 原理

DES是一种将64比特的明文加密成64比特的密文的对称密码算法,它的密钥长度是56比特。从规格上来说,DES的密钥长度是64比特,但由于每隔7比特会设置一个用于错误检查的比特,因此实质上其密钥长度是56比特。
DES以64比特的明文(比特序列)为一个单位来进行加密。这个64比特的单位称为分组。一般来说,以分组为单位进行处理的密码算法称为分组密码(blockcipher),DES就是分组密码的一种。
如果要加密的明文比较长,就需要对DES加密进行迭代(反复),而迭代的具体方式就称为模式(mode)。

下面是DES的加密解密工作原理图:
在这里插入图片描述
那么DES具体是如何加密的呢? DES加密的时候使用的Feistel网络结构。在网络中,加密的各个步骤称为轮(round),整个加密过程就是进行若干轮次的循环。在DES中,进行了16次的轮循环。其中每一次的循环过程如下:
流程如下:

  1. 初始置换明文
  2. 将输入的明文等分为左右两部分,每部分32比特
  3. 将输入的右侧直接直接输出
  4. 将输入的右侧发送到轮函数。
  5. 轮函数配合子密钥对右侧的比特进行加密,计算出一串随机的比特序列
  6. 将计算出的比特序列与左侧的数据进行XOR运算,并输出到右侧。
  7. 下一轮加密开始时,交换左右两侧的数据,继续进行3-6的步骤。

2.3 Go实现DES源码浅析

在Go的crypto/des中实现了DES加密,而我们使用DES进行加密时,仅仅调用包中的一些API就可以了。

// 创建DES加密对象
// 参数key为密钥
func NewCipher(key []byte) (cipher.Block, error) {
	if len(key) != 8 {
		return nil, KeySizeError(len(key))
	}

	c := new(desCipher)
	// 生成16个子密钥
	c.generateSubkeys(key)
	return c, nil
}

// 加密
func (c *desCipher) Encrypt(dst, src []byte) {
	// 判断明文长度是否为8字节
	if len(src) < BlockSize {
		panic("crypto/des: input not full block")
	}
	// 小于8字节
	if len(dst) < BlockSize {
		panic("crypto/des: output not full block")
	}
	// 判断存储密文的变量是否可用
	if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
		panic("crypto/des: invalid buffer overlap")
	}
	// 加密
	encryptBlock(c.subkeys[:], dst, src)
}

func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) {
	// 大端排序
	b := binary.BigEndian.Uint64(src)
	// 初始置换明文
	b = permuteInitialBlock(b)
	// 分成左右两部分
	left, right := uint32(b>>32), uint32(b)

	left = (left << 1) | (left >> 31)
	right = (right << 1) | (right >> 31)

	if decrypt {
		for i := 0; i < 8; i++ {
			left, right = feistel(left, right, subkeys[15-2*i], subkeys[15-(2*i+1)])
		}
	} else {
		// 进行Feistel轮循环
		for i := 0; i < 8; i++ {
			left, right = feistel(left, right, subkeys[2*i], subkeys[2*i+1])
		}
	}

	left = (left << 31) | (left >> 1)
	right = (right << 31) | (right >> 1)

	// switch left & right and perform final permutation
	// 进行最后的数据置换
	preOutput := (uint64(right) << 32) | uint64(left)
	binary.BigEndian.PutUint64(dst, permuteFinalBlock(preOutput))
}

2.4 案例

2.4.1 简单加解密

func desEncrypt() {

	key := []byte("12345678")
	// 因为DES的密钥长度为64,所以一次只能加密8字节的数据
	src := []byte("12345678")
	block, err := des.NewCipher(key)
	if err != nil {
		fmt.Println(err)
		return
	}

	// 加密
	block.Encrypt(src, src)

	// 解密
	block.Decrypt(src, src)
}

上面的代码只是实现的简单的加解密,如果想要更复杂的操作那么就需要用到模式这个技术。下面介绍一下模式,然后看看怎么在DES中使用模式。

3. 模式

3.1 介绍

密码算法也可以分为分组密码和流密码两种。分组密码(block cipher)是每次只能处理特定的一块数据的密码算法,这里的一块就称为分组(block)。流密码(stream cipher)是对数据流进行连续处理的密码算法。流密码一般以1比特、8比特和32比特等为单位进行加密和解密。

分组密码算法只能加密固定长度的分组,但是我们需要加密的明文长度可能超过分组密码的分组长度,这时候就需要对分组密码算法进行迭代,以便将这一段很长的明文全部加密。而迭代的方法就称为分组密码的模式(mode)。

模式有以下几种

  • ECB模式:Electronic CodeBook mode,电子密码本模式
  • CBC模式:Cipher Block Chaining mode,密码分组链接模式
  • CFB模式:Cipher FeedBack mode,密文反馈模式
  • OFB模式:Output FeedBack mode,输出反馈模式
  • CTR模式:CounTeR mode,计数器模式

3.2 ECB模式

将明文分组直接加密的方式就是ECB模式,这种模式非常简单,但由于存在弱点,因此通常不会使用这种模式。而且在Go中,不提供ECB模式的API。

其工作原理如下:
在这里插入图片描述ECB模式是所有模式中最简单的一种。ECB模式中,明文分组与密文分组是一一对应的关系,因此,如果明文中存在多个相同的明文分组,则这些明文分组最终都将被转换为相同的密文分组。这样一来,只要观察一下密文,就可以知道明文中存在怎样的重复组合,并可以以此为线索来破译密码。

3.3 CBC模式

在CBC模式中,首先将明文分组与前一个密文分组进行XOR运算,然后再进行加密。这样就可以避免ECB模式的弱点。

其工作原理如下:
在这里插入图片描述第一个明文进行分组时,并没有前一个密文的分组,所以需要一个初始化向量(IV)。一般来说,每次加密都会随机产生一个不同的比特序列来作为初始化向量。

CBC模式是一种最常用的加密模式,它主要缺点是加密是连续的,不能并行处理,并且与ECB一样消息块必须填充到块大小的整倍数。

3.4 CFB模式

在CFB模式中,前一个密文分组会被送回到密码算法的输入端。所谓的反馈就是密文分组被返回到输入端的意思。

其工作原理如下:
在这里插入图片描述在这里插入图片描述在ECB模式和CBC模式中,明文分组都是通过密码算法进行加密的,然而,在CFB模式中,明文分组并没有通过密码算法来直接进行加密。而是把明文分组和加密后的初始化向量进行XOR操作。

3.5 OFB模式

在OFB模式中,密码算法的输出会反馈到密码算法的输入中, 即上一个分组密码算法的输出是当前分组密码算法的输入。
在这里插入图片描述在OFB模式中,XOR所需要的比特序列(密钥流)可以事先通过密码算法生成,和明文分组无关。只要提前准备好所需的密钥流,则在实际从明文生成密文的过程中,就完全不需要动用密码算法了。只要将明文与密钥流进行XOR就可以了。

3.6 CTR模式

CTR摸式是一种通过将逐次累加的计数器进行加密来生成密钥流的流密码。
在CTR模式中,每个分组对应一个逐次累加的计数器,并通过对计数器进行加密来生成密钥流。也就是说,最终的密文分组是通过将计数器加密得到的比特序列,与明文分组进行XOR而得到的。

其工作原理如下:
在这里插入图片描述在这里插入图片描述

CTR模式能够以任意顺序处理分组,意味着能够实现并行计算。在支持并行计算的系统中,CTR模式的速度非常快。

3.7 使用CBC实模式现DES

// 加密
// plainText: 密文
// key: 密钥
func desCBCEncrypt(key []byte, plainText[]byte) []byte {
	// 创建DES加密对象
	block, err := des.NewCipher(key)
	if err != nil {
		fmt.Println("new des block err: ", err)
	}
	// 初始化向量
	iv := []byte("12345678")
	// 如果字节长度不符合要求,就需要进行填充
	newText := paddingLastGroup(plainText, block.BlockSize())
	// 创建CBC模式
	blockModel := cipher.NewCBCEncrypter(block, iv)
	// 加密
	blockModel.CryptBlocks(newText, newText)
	return newText
}

// 解密
// cipherText: 密文
// key: 密钥
func desCBCDecrypt(cipherText []byte, key []byte) {
	// 创建DES加密对象
	block, err := des.NewCipher(key)
	if err != nil {
		fmt.Println("newCipher err: ", err)
		return
	}
	// 初始化向量
	iv := []byte("12345678")
	// 创建CBC模式
	blockModel := cipher.NewCBCDecrypter(block, iv)
	// 解密
	blockModel.CryptBlocks(cipherText, cipherText)
}

3.8 使用CTR模式实现DES

// CTR模式DES加密
func desCTREncrypt(plainText []byte, key []byte) []byte {
	// 获取DES对象
	block, err := des.NewCipher(key)
	if err != nil {
		fmt.Println("new triple des err: ", err)
		return nil
	}

	// 创建CTR模式
	stream := cipher.NewCTR(block, key[:8])
	// 加密
	cipherText := make([]byte, len(plainText))
	stream.XORKeyStream(cipherText, plainText)

	return cipherText
}

// CTR模式DES解密
func desCTRDecrypt(cipherText []byte, key []byte) []byte {
	// 获取DES对象
	block, err := des.NewCipher(key)
	if err != nil {
		fmt.Println("new triple des err: ", err)
		return nil
	}

	// 创建CTR模式
	stream := cipher.NewCTR(block, key[:8])
	// 解密
	plainText := make([]byte, len(cipherText))
	stream.XORKeyStream(plainText, cipherText)

	return plainText
}

4. 3DES

4.1 介绍

现在的DES已经可以在现实的时间内被暴力破解,因此需要一种来代替DES的分组密码,三重DES就是出于这个目的被开发出来的。
三重DES(triple DES)增加了DES的强度,是将DES重复3次的一种密码算法,也称为TDEA(Triple Data Encryption Algorithm),通常缩写为3DES。
由于DES密钥的长度实质上是56比特。因此3DES的密钥长度就是168比特。
三重DES并不是进行三次DES加密,而是加密—>解密—>解密的过程。其中加入解密的操作是为了让3DES能够兼容普通的DES。
尽管3DES目前还被银行等机构使用,但其处理速度不高,除了特别重视向下兼容性的情况外,很少用于新的用途。

其加密流程如下:
在这里插入图片描述

4.2 3DES的简单加解密

func tripleDES(plainText []byte, key []byte) {
	// 获取3DES对象
	block, err := des.NewTripleDESCipher(key)
	if err != nil {
		fmt.Println("new triple des err: ", err)
		return
	}

	// 加密
	cipherText := make([]byte, len(plainText))
	block.Encrypt(cipherText, plainText)
	fmt.Printf("简单3DES加密后的数据:%s\n", cipherText)

	// 解密
	block.Decrypt(plainText, cipherText)
	fmt.Printf("简单3DES解密后的数据:%s\n", plainText)
}

4.3 使用CBC模式

// CBC模式加密
func tripleDESCBCEncrypt(plainText []byte, key []byte) []byte {
	// 获取3DES对象
	block, err := des.NewTripleDESCipher(key)
	if err != nil {
		fmt.Println("new triple des err: ", err)
		return nil
	}

	// 对明文进行填充
	newPlainText := paddingLastGroup(plainText, block.BlockSize())
	// 获取CBC模式对象
	blockMode := cipher.NewCBCEncrypter(block, key[:8])
	cipherText := make([]byte, len(newPlainText))
	// 加密
	blockMode.CryptBlocks(cipherText, newPlainText)

	return cipherText
}

// CBC模式解密
func tripleDESCBCDecrypt(cipherText []byte, key []byte) []byte {
	// 获取3DES对象
	block, err := des.NewTripleDESCipher(key)
	if err != nil {
		fmt.Println("new triple des err: ", err)
		return nil
	}

	// 获取CBC模式对象
	blockMode := cipher.NewCBCDecrypter(block, key[:8])
	// 解密
	blockMode.CryptBlocks(cipherText, cipherText)
	// 去除填充数据
	plainText := unPaddingLastGrooup(cipherText)

	return plainText
}

4.4 使用CTR模式

// CTR模式加密
func tripleDESCTREncrypt(plainText []byte, key []byte) []byte {
	// 获取3DES对象
	block, err := des.NewTripleDESCipher(key)
	if err != nil {
		fmt.Println("new triple des err: ", err)
		return nil
	}

	// 创建CTR模式
	stream := cipher.NewCTR(block, key[:8])

	// 加密
	cipherText := make([]byte, len(plainText))
	stream.XORKeyStream(cipherText, plainText)

	return cipherText
}

// CTR模式解密
func tripleDESCTRDecrypt(cipherText []byte, key []byte) []byte {
	// 获取3DES对象
	block, err := des.NewTripleDESCipher(key)
	if err != nil {
		fmt.Println("new triple des err: ", err)
		return nil
	}

	// 创建CTR模式
	stream := cipher.NewCTR(block, key[:8])

	// 解密
	plainText := make([]byte, len(cipherText))
	stream.XORKeyStream(plainText, cipherText)

	return plainText
}

测试结果:
在这里插入图片描述

5. AES

5.1 介绍

AES(Advanced Encryption Standard)是取代其前任标准DES而成为新标准的一种对称密码算法。AES的分组固定长度为128比特,密钥长度可以为128、192和256。在Go提供的接口中,密钥长度只能是128。

5.2 加密解密

和DES—样,AES算法也是由多个轮所构成的,其中每一个轮分为SubBytes、ShiftRows、MixColumns和AddRoundKey这4个步骤。DES使用Feistel网络作为其基本结构,而AES使用了SPN结构。
其加密流程如下:

  1. AES的输入分组为128比特,首先对数据进行分组输入,也就是分组输入16字节的数据。
  2. 对这16个字节逐个进行SubBytes处理。SubBytes就是以每个字节的值为索引,从一张拥有256个值的替换表中查找出对应得值。类似于简单密码替换技术。
  3. SubBytes之后需要进行ShiftRows处理。将以4字节单位的行安装一定的规则向左平移,且平移的字节数是不同的。
  4. ShiftRows之后需要进行MixColumns处理。这一步是对一个4字节的值进行比特运算,将其变为另外一个4字节值。
  5. 最后进行AddRoundKey处理。就是将MixColumns的输出于轮密码进行XOR。这样,AES的一轮加密就完成了。
  6. 进行循环上面的步骤10~14次。

5.3 简单应用

// 加密
func aesEncrypt(plainText []byte, key []byte) []byte {
	// 获取AES加密对象
	block, err := aes.NewCipher(key)
	if err != nil {
		fmt.Println("new aes cipher err: ", err)
		return nil
	}

	// 加密
	cipherText := make([]byte, 16)
	block.Encrypt(cipherText, plainText)

	return cipherText
}

// 解密
func aesDecrypt(cipherText []byte, key []byte) []byte {
	// 获取AES加密对象
	block, err := aes.NewCipher(key)
	if err != nil {
		fmt.Println("new aes cipher err: ", err)
		return nil
	}
	// 解密
	plainText := make([]byte, 16)
	block.Decrypt(plainText, cipherText)

	return plainText
}

5.4 使用CTR模式

// 加密
func aesCTREncrypt(plainText []byte, key []byte) []byte {
	// 获取AES对象
	block, err := aes.NewCipher(key)
	if err != nil {
		fmt.Println("new aes cipher err: ", err)
		return nil
	}

	// 获取CTR模式对象
	iv := []byte("1234567812345678")

	stream := cipher.NewCTR(block, iv)

	// 加密
	cipherText := make([]byte, len(plainText))
	stream.XORKeyStream(cipherText, plainText)

	return cipherText
}

// 解密
func aesCTRDecrypt(cipherText []byte, key []byte) []byte {

	// 获取AES对象
	block, err := aes.NewCipher(key)
	if err != nil {
		fmt.Println("new aes cipher err: ", err)
		return nil
	}

	iv := []byte("1234567812345678")
	stream := cipher.NewCTR(block, iv)

	plainText := make([]byte, 1000)
	stream.XORKeyStream(plainText, cipherText)

	return plainText
}

测试结果:
在这里插入图片描述

6. 最后

文章大部分参考了《图解密码技术》一书,感觉这本书挺不错的,简单易懂。如果需要的话可以私信我。代码已经上传到GitHub上面。https://github.com/bigzoro/cryptography/tree/main/symmetricalCryption

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值