二、对称加密

  • 对称加密:也称为对称密码,是指在加密和解码时使用同一秘钥的加密方式

1.DES

1.1什么是DES
  • DES一直以来被美国以及其他国家的政府和银行等广泛使用。然而,随着计算机的进步,现在DES已经能够被暴力破解,强度大不如前了。
  • 由于DES的密文可以在短时间内被破译,因此除了用它来解密以前的密文以外,现在我们不应该再使用DES了。
1.2加密和解密
  • DES是一种将64比特的明文加密成64比特的密文的对称密码算法,它的密钥长度是56比特。
  • 尽管从规格上来说,DES的密钥长度是64比特,但由于每隔7比特会设置一个用于错误检查的比特,因此实质上其密钥长度是56比特。
  • DES是以64比特的明文(比特序列)为一个单位来进行加密的,这个64比特的单位称为分组。一般来说,以分组为单位进行处理的密码算法称为分组密码(blockcipher),DES就是分组密码的一种。
  • DES每次只能加密64比特的数据,如果要加密的明文比较长,就需要对DES加密进行迭代(反复),而迭代的具体方式就称为模式(mode)。
1.3Go中对DES的操作
package main

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

/*
	DES加密代码:
	src -> 要加密的明文
	key -> 秘钥, 大小为: 8byte
 */
func DesEncrypt_CBC(src, key []byte) []byte{
	// 1. 创建并返回一个使用DES算法的cipher.Block接口
	block, err := des.NewCipher(key)
	// 2. 判断是否创建成功
	if err != nil{
		panic(err)
	}
	// 3. 对最后一个明文分组进行数据填充
	src = PKCS5Padding(src, block.BlockSize())
	// 4. 创建一个密码分组为链接模式的, 底层使用DES加密的BlockMode接口
	//    参数iv的长度, 必须等于b的块尺寸
	tmp := []byte("helloDES")
	blackMode := cipher.NewCBCEncrypter(block, tmp)
	// 5. 加密连续的数据块
	dst := make([]byte, len(src))
	blackMode.CryptBlocks(dst, src)

	// 6. 将加密数据返回
	return dst
}

/*
	DES解密代码:
	src -> 要解密的密文
	key -> 秘钥, 和加密秘钥相同, 大小为: 8byte
*/
func DesDecrypt_CBC(src, key []byte) []byte {
	// 1. 创建并返回一个使用DES算法的cipher.Block接口
	block, err := des.NewCipher(key)
	// 2. 判断是否创建成功
	if err != nil{
		panic(err)
	}
	// 3. 创建一个密码分组为链接模式的, 底层使用DES解密的BlockMode接口
	tmp := []byte("helloDES")
	blockMode := cipher.NewCBCDecrypter(block, tmp)
	// 4. 解密数据
	dst := src
	blockMode.CryptBlocks(src, dst)
	// 5. 去掉最后一组填充的数据
	dst = PKCS5UnPadding(dst)

	// 6. 返回结果
	return dst
}

// 使用pks5的方式填充
func PKCS5Padding(ciphertext []byte, blockSize int) []byte{
	// 1. 计算最后一个分组缺多少个字节
	padding := blockSize - (len(ciphertext)%blockSize)
	// 2. 创建一个大小为padding的切片, 每个字节的值为padding
	padText := bytes.Repeat([]byte{byte(padding)}, padding)
	// 3. 将padText添加到原始数据的后边, 将最后一个分组缺少的字节数补齐
	newText := append(ciphertext, padText...)
	return newText
}

// 删除pks5填充的尾部数据
func PKCS5UnPadding(origData []byte) []byte{
	// 1. 计算数据的总长度
	length := len(origData)
	// 2. 根据填充的字节值得到填充的次数
	number := int(origData[length-1])
	// 3. 将尾部填充的number个字节去掉
	return origData[:(length-number)]
}

//测试
func main() {
	// 加密
	key := []byte("12345678")
	result := DesEncrypt_CBC([]byte("床前明月光, 疑是地上霜. 举头望明月, 低头思故乡."), key)
	fmt.Println("DES加密之后的数据: ", result)
	fmt.Println("DES加密之后的base64数据: ",base64.StdEncoding.EncodeToString(result))
	// 解密
	result = DesDecrypt_CBC(result, key)
	fmt.Println("DES解密之后的数据: ", string(result))
}
  • 输出结果:
DES加密之后的数据:  [138 96 138 90 95 68 238 159 74 1 198 0 245 60 148 97 137 168 253 7 111 67 20 209 47 195 26 42 22 63 205 164 110 252 67 165 53 157 216 152 190 49 92 26 189 228 80 13 195 11 237 86 15 251 98 232 178 15 153 117 180 241 11 30 224 204 151 165 51 105 189 248]
DES加密之后的base64数据:  imCKWl9E7p9KAcYA9TyUYYmo/QdvQxTRL8MaKhY/zaRu/EOlNZ3YmL4xXBq95FANwwvtVg/7YuiyD5l1tPELHuDMl6Uzab34
DES解密之后的数据:  床前明月光, 疑是地上霜. 举头望明月, 低头思故乡.

2.三重DES

2.1三重DES的加密
  • 现在DES已经可以在现实的时间内被暴力破解,因此我们需要一种用来替代DES的分组密码,三重DES就是出于这个目的被开发出来的。
  • 三重DES(triple-DES)是为了增加DES的强度,将DES重复3次所得到的一种密码算法,通常缩写为3DES
  • 3DES加密过程: 秘钥1 -> 加密, 秘钥2 -> 解密, 秘钥3 -> 加密
  • 3DES解密过程: 秘钥1 -> 解密, 秘钥2 -> 加密, 秘钥3 -> 解密
  • 明文经过三次DES处理才能变成最后的密文,由于DES密钥的长度实质上是56比特,因此三重DES的密钥长度就是56×3=168比特, 加上用于错误检测的标志位8x3, 共192bit
  • 三重DES并不是进行三次DES加密(加密–>加密–>加密),而是加密–>解密–>加密的过程。在加密算法中加人解密操作让人感觉很不可思议,实际上这个方法是IBM公司设计出来的,目的是为了让三重DES能够兼容普通的DES。
  • 当三重DES中所有的密钥都相同时,三重DES也就等同于普通的DES了。这是因为在前两步加密–>解密之后,得到的就是最初的明文。因此,以前用DES加密的密文,就可以通过这种方式用三重DES来进行解密。也就是说,三重DES对DES具备向下兼容性。
  • 如果密钥1和密钥3使用相同的密钥,而密钥2使用不同的密钥(也就是只使用两个DES密钥),这种三重DES就称为DES-EDE2。EDE表示的是加密(Encryption) -->解密(Decryption)–>加密(Encryption)这个流程。
  • 密钥1、密钥2、密钥3全部使用不同的比特序列的三重DES称为DES-EDE3。
  • 尽管三重DES目前还被银行等机构使用,但其处理速度不高,而且在安全性方面也逐渐显现出了一些问题。
2.2Go中对3DES的操作
package main

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

// 3DES加密
func TripleDESEncrypt(src, key []byte) []byte {
	// 1. 创建并返回一个使用3DES算法的cipher.Block接口
	block, err := des.NewTripleDESCipher(key)
	if err != nil {
		panic(err)
	}
	// 2. 对最后一组明文进行填充
	src = PKCS5Padding(src, block.BlockSize())
	// 3. 创建一个密码分组为链接模式, 底层使用3DES加密的BlockMode模型
	blockMode := cipher.NewCBCEncrypter(block, key[:8])
	// 4. 加密数据
	dst := src
	blockMode.CryptBlocks(dst, src)
	return dst
}

// 3DES解密
func TripleDESDecrypt(src, key []byte) []byte {
	// 1. 创建3DES算法的Block接口对象
	block, err := des.NewTripleDESCipher(key)
	if err != nil {
		panic(err)
	}
	// 2. 创建密码分组为链接模式, 底层使用3DES解密的BlockMode模型
	blockMode := cipher.NewCBCDecrypter(block, key[:8])
	// 3. 解密
	dst := src
	blockMode.CryptBlocks(dst, src)
	// 4. 去掉尾部填充的数据
	dst = PKCS5UnPadding(dst)
	return dst
}

//测试
func main() {
	// 加密
	key := []byte("123456781234567812345678")
	result := TripleDESEncrypt([]byte("床前明月光, 疑是地上霜. 举头望明月, 低头思故乡."), key)
	fmt.Println("3DES加密之后的数据: ", result)
	fmt.Println("3DES加密之后的base64数据: ", base64.StdEncoding.EncodeToString(result))
	// 解密
	result = TripleDESDecrypt(result, key)
	fmt.Println("3DES解密之后的数据: ", string(result))
}
  • 输出结果
3DES加密之后的数据:  [212 59 108 139 6 206 6 70 206 171 66 58 68 10 124 83 26 170 200 186 97 118 239 165 98 173 43 164 158 24 72 197 41 168 46 173 72 104 31 54 68 89 175 143 81 217 218 200 21 185 89 61 56 129 155 27 143 56 210 125 117 84 92 138 150 189 99 237 39 18 130 187]
3DES加密之后的base64数据:  1DtsiwbOBkbOq0I6RAp8UxqqyLphdu+lYq0rpJ4YSMUpqC6tSGgfNkRZr49R2drIFblZPTiBmxuPONJ9dVRcipa9Y+0nEoK7
3DES解密之后的数据:  床前明月光, 疑是地上霜. 举头望明月, 低头思故乡.

3.AES

  • 全世界的企业和密码学家提交了多个对称密码算法作为AES的候选,最终在2000年从这些候选算法中选出了一种名为==Rijndael==的对称密码算法,并将其确定为了AES。
  • Rijndael是由比利时密码学家Joan Daemen和Vincent Rijmen设汁的分组密码算法,今后会有越来越多的密码软件支持这种算法。
  • Rijndael的分组长度为128比特,也就是16字节。密钥长度可以以32比特为单位在128比特到256比特的范围内进行选择(不过==在AES的规格中,密钥长度只有128、192和256比特三种==)。
3.1AES的加密和解密
  • 首先,需要逐个字节地对16字节的输入数据进行SubBytes处理。所谓SubBytes,就是以每个字节的值(0~255中的任意值)为索引,从一张拥有256个值的替换表(S-Box)中查找出对应值的处理,也是说,将一个1字节的值替换成另一个1字节的值。
  • SubBytes之后需要进行ShiftRows处理,即将SubBytes的输出以字节为单位进行打乱处理。这种打乱处理是有规律的。
  • ShiftRows之后需要进行MixCo1umns处理,即对一个4字节的值进行比特运算,将其变为另外一个4字节值。
  • 最后,需要将MixColumns的输出与轮密钥进行XOR,即进行AddRoundKey处理。到这里,AES的一轮就结東了。实际上,在AES中需要重复进行10 ~ 14轮计算。
3.2Go中对AES的使用
package main

import (
	"crypto/aes"
	"crypto/cipher"
	"bytes"
	"fmt"
	"encoding/base64"
)

// AES加密
func AESEncrypt(src, key []byte) []byte{
	// 1. 创建一个使用AES加密的块对象
	block, err := aes.NewCipher(key)
	if err != nil{
		panic(err)
	}
	// 2. 最后一个分组进行数据填充
	src = PKCS5Padding(src, block.BlockSize())
	// 3. 创建一个分组为链接模式, 底层使用AES加密的块模型对象
	blockMode := cipher.NewCBCEncrypter(block, key[:block.BlockSize()])
	// 4. 加密
	dst := src
	blockMode.CryptBlocks(dst, src)
	return dst
}

// AES解密
func AESDecrypt(src, key []byte) []byte{
	// 1. 创建一个使用AES解密的块对象
	block, err := aes.NewCipher(key)
	if err != nil{
		panic(err)
	}
	// 2. 创建分组为链接模式, 底层使用AES的解密模型对象
	blockMode := cipher.NewCBCDecrypter(block, key[:block.BlockSize()])
	// 3. 解密
	dst := src
	blockMode.CryptBlocks(dst, src)
	// 4. 去掉尾部填充的字
	dst = PKCS5UnPadding(dst)
	return dst
}

//测试
func main() {
	// 加密
	key := []byte("1234567812345678")
	result := AESEncrypt([]byte("床前明月光, 疑是地上霜. 举头望明月, 低头思故乡."), key)
	fmt.Println("AES加密之后的数据: ", result)
	fmt.Println("AES加密之后的base64数据: ", base64.StdEncoding.EncodeToString(result))
	// 解密
	result = AESDecrypt(result, key)
	fmt.Println("AES解密之后的数据: ", string(result))
}
  • 输出结果:
AES加密之后的数据:  [250 223 177 140 253 90 31 5 94 80 76 69 42 241 251 120 128 139 11 97 7 219 14 32 137 219 12 86 163 19 254 166 157 1 202 230 15 92 14 189 156 193 117 121 246 63 28 75 167 130 199 227 114 123 157 40 20 223 124 65 242 85 122 87 41 9 226 173 109 227 201 27 188 68 17 241 131 63 206 239]
AES加密之后的base64数据:  +t+xjP1aHwVeUExFKvH7eICLC2EH2w4gidsMVqMT/qadAcrmD1wOvZzBdXn2PxxLp4LH43J7nSgU33xB8lV6VykJ4q1t48kbvEQR8YM/zu8=
AES解密之后的数据:  床前明月光, 疑是地上霜. 举头望明月, 低头思故乡.

4.应选择哪种对称加密

    1. 今后最好不要将DES用于新的用途,因为随着计算机技术的进步,现在用暴力破解法已经能够在现实的时间内完成对DES的破译。但是,在某些情况下也需要保持与旧版本软件的兼容性。
    1. 出于兼容性的因素三重DES在今后还会使用一段时间,但会逐渐被AES所取代。
    1. 今后大家应该使用的算法是AES(Rijndael),因为它安全、快速,而且能够在各种平台上工作。此外,由于全世界的密码学家都在对AES进行不断的验证,因此即便万一发现它有什么缺陷,也会立刻告知全世界并修复这些缺陷。
  • 然而,用对称密码进行通信时,还会出现密钥的配送问题,即如何将密钥安全地发送给接收者。为了解决密钥配送问题,我们需要非对称加密技术
  • 这里所介绍的几乎所有的密码算法,都只能将一个固定长度的分组进行加密当需要加密的明文长度超过分组长度时,就需要对密码算法进行迭代,后面将探讨对分组密码进行迭代的方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值