Golang对称加密之模式问题实战荐

释放双眼,带上耳机,听听看~!

1. 背景

近期项目在对接第三方产品,传输过程中涉及到数据加密,  数据加密流程为:

  • 发送数据DES加密

  • DES加密后的数据进行base64编码

  • 发送,接受数据

  • 接受读取的数据进行base64解码

  • base64解码完的数据机型DES解密

    由于采用golang对接,文档且无说明情况下,默认采用CBC模式加解密,导致很长时间对接不上。
    后通过对方Java Demo代码查看得知采用ECB加密模式,Java默认DES算法使用DES/ECB/PKCS5Padding工作方式,在GO语言中因为ECB的脆弱性,DES的ECB模式是故意不放出来的,但实际情况中有时我们并不需要那么安全。

DES介绍

DES 使用一个 56 位的密钥以及附加的 8 位奇偶校验位,产生最大 64 位的分组大小。这是一个迭代的分组密码,使用称为 Feistel 的技术,其中将加密的文本块分成两半。使用子密钥对其中一半应用循环功能,然后将输出与另一半进行”异或”运算;接着交换这两半,这一过程会继续下去,但最后一个循环不交换。DES 使用 16 个循环,使用异或,置换,代换,移位操作四种基本运算。

DES常见加密模式

CBC(加密分组链接模式)

密文分组链接方式,这是golang和.NET封装的DES算法的默认模式,它比较麻烦,加密步骤如下:

  1. 首先将数据按照8个字节一组进行分组得到D1D2……Dn(若数据不是8的整数倍,就涉及到数据补位了)

  2. 第一组数据D1与向量I异或后的结果进行DES加密得到第一组密文C1(注意:这里有向量I的说法,ECB模式下没有使用向量I)

  3. 第二组数据D2与第一组的加密结果C1异或以后的结果进行DES加密,得到第二组密文C2

  4. 之后的数据以此类推,得到Cn

  5. 按顺序连为C1C2C3……Cn即为加密结果。

  • 数据补位一般有NoPadding和PKCS7Padding(JAVA中是PKCS5Padding)填充方式,PKCS7Padding和PKCS5Padding实际只是协议不一样,根据相关资料说明:PKCS5Padding明确定义了加密块是8字节,PKCS7Padding加密快可以是1-255之间。但是封装的DES算法默认都是8字节,所以可以认为他们一样。数据补位实际是在数据不满8字节的倍数,才补充到8字节的倍数的填充过程。

  • NoPadding填充方式:算法本身不填充,比如.NET的padding提供了有None,Zeros方式,分别为不填充和填充0的方式。

  • PKCS7Padding(PKCS5Padding)填充方式:为.NET和JAVA的默认填充方式,对加密数据字节长度对8取余为r,如r大于0,则补8-r个字节,字节为8-r的值;如果r等于0,则补8个字节8。比如:

加密字符串为为AAA,则补位为AAA55555;加密字符串为BBBBBB,则补位为BBBBBB22;加密字符串为CCCCCCCC,则补位为CCCCCCCC88888888。

ECB(电子密码本模式)

电子密本方式,这是JAVA封装的DES算法的默认模式,就是将数据按照8个字节一段进行DES加密或解密得到一段8个字节的密文或者明文,最后一段不足8个字节,则补足8个字节(注意:这里就涉及到数据补位了)进行计算,之后按照顺序将计算所得的数据连在一起即可,各段数据之间互不影响。

CFB(加密反馈模式)

加密反馈模式克服了需要等待8个字节才能加密的缺点,它采用了分组密码作为流密码的密钥流生成器;

OFB(输出反馈模式)

与CFB模式不同之处在于, 加密位移寄存器与密文无关了,仅与加密key和加密算法有关;
做法是不再把密文输入到加密移位寄存器,而是把输出的分组密文(Oi)输入到一位寄存器;

DES加密之golang的CBC和ECB模式代码实现

CBC和ECB模式加密

func DesECBEncrypt(data, key []byte)([]byte, error) {    block, err := des.NewCipher(key)    if err != nil {        return nil, err    }    bs := block.BlockSize()    data = PKCS5Padding(data, bs)    if len(data)%bs != 0 {        return nil, errors.New("Need a multiple of the blocksize")    }    out := make([]byte, len(data))    dst := out    for len(data) > 0 {        block.Encrypt(dst, data[:bs])        data = data[bs:]        dst = dst[bs:]    }    return out, nil}func DesCBCEncrypt(origData, key []byte) ([]byte, error) {    block, err := des.NewCipher(key)    if err != nil {        return nil, err    }    origData = PKCS5Padding(origData, block.BlockSize())    // origData = ZeroPadding(origData, block.BlockSize())    blockMode := cipher.NewCBCEncrypter(block, key)    crypted := make([]byte, len(origData))    // 根据CryptBlocks方法的说明,如下方式初始化crypted也可以    // crypted := origData    blockMode.CryptBlocks(crypted, origData)    return crypted, nil}func PKCS5Padding(ciphertext []byte, blockSize int) []byte {    padding := blockSize - len(ciphertext)%blockSize    padtext := bytes.Repeat([]byte{byte(padding)}, padding)    return append(ciphertext, padtext...)}

CBC和ECB模式解密

func DesECBDecrypt(data, key []byte)([]byte, error) {    block, err := des.NewCipher(key)    if err != nil {        return nil, err    }    bs := block.BlockSize()    if len(data)%bs != 0 {        return nil, errors.New("crypto/cipher: input not full blocks")    }    out := make([]byte, len(data))    dst := out    for len(data) > 0 {        block.Decrypt(dst, data[:bs])        data = data[bs:]        dst = dst[bs:]    }    out = PKCS5UnPadding(out)    return out, nil}func DesCBCDecrypt(crypted, key []byte) ([]byte, error) {    block, err := des.NewCipher(key)    if err != nil {        return nil, err    }    blockMode := cipher.NewCBCDecrypter(block, key)    //origData := make([]byte, len(crypted))    origData := crypted    blockMode.CryptBlocks(origData, crypted)    //origData = PKCS5UnPadding(origData)    origData = PKCS5UnPadding(origData)    return origData, nil}func PKCS5UnPadding(origData []byte) []byte {    length := len(origData)    unpadding := int(origData[length-1])    return origData[:(length - unpadding)]}

总结

以需求驱动技术,技术本身没有优略之分,只有业务之分。

【转自慕课】https://www.imooc.com

Go

go基础系列:数组

2022-3-3 0:39:30

Go

Go语言6-接口、反射

2022-3-3 0:42:49

搜索