6 Go 密码学(三)对称加密 DES、TDES、AES

一、对称加密概述

我们在开发中常会遇到这种需求:通信的两端需要传输安全级别较高的数据,这需要我们传输的加密数据既要难以破解,又要可逆的解密过程。哈希算法虽难以破解,但并非适用于通信中的加解密传输。这就需要基于秘钥管理的加密技术了。对称加密是最简单快速的加密方式,所谓对称加密,即加解密双方都掌握相同的秘钥,通过同一秘钥完成加解密操作。常用的对称加密算法有:

  • DES:Data Encryption Standard,即数据加密标准;
  • TDES(TripleDES) :三重DES;
  • AES:Rijndael加密法,Advanced Encryption Standard高级加密标准。

Go标准库中的加密相关包都有相应的支持,下面我们来逐一演示:

二、DES对称加密

DES算法原理:明文按64位进行分组,密钥长64位,密钥事实上是56位参与DES运算(第8、16、24、32、40、48、56、64位是校验位, 使得每个密钥都有奇数个1)分组后的明文组和56位的密钥按位替代或交换的方法形成密文组的加密方法。

所以要实现DES加密需要准备如下要求:

  • ==准备一个8字节(64Bit)的秘钥==;
  • 原文需要按8字节(64Bit)长度进行分组,共有五种分组模式,此处我们不具体展开,我们用常用的CBC(密码分组链接模式),这种模式需要一个与分组长度相同的初始化向量,有兴趣的可搜索了解其他模式;
  • 对末尾的未够8字节的数据进行填充,把原文转为64Bit的整数倍;
  • ==实现加密时的未位分组填充算法和解密时的删除密文末尾分组算法。==

以上,秘钥和初始化向量值需要自己管理,末尾分组的填充算法和删除算法需要自己实现。下面我们实现一个填充算法和删除算法:

//填充明文最后一个分组工具方法
//src - 原始数据
//blockSize - 每个分组的数据长度
func paddingBytes(src []byte, blockSize int) []byte {
    //1.求出最后一个分组要填充多个字节
    padding := blockSize - len(src)%blockSize
    //2.创建新的切片,切片的字节数为填充的字节数,并初始化化,每个字节的值为填充的字节数
    padBytes := bytes.Repeat([]byte{byte(padding)}, padding)
    //3.将创建出的新切片和原始数据进行连接
    newBytes := append(src, padBytes...)

    //4.返回新的字符串
    return newBytes
}

//删除密文末尾分组填充的工具方法
func unPaddingBytes(src []byte) []byte {
    //1.求出要处理的切片的长度
    l := len(src)
    //2.取出最后一个字符,得到其整型值
    n := int(src[l-1])

    //3.将切片末尾的number个字节删除
    return src[:l-n]
}

以上实现思路为:计算出最后一个分组需要填充的字节数(1-8),并把这个数作为需要填充的占位符,再把填充后的分组与源数据拼接就得到待加密的字符串。

Go通过crypto/des包支持DES加密算法:


import (
    "bytes"
    "crypto/cipher"
    "crypto/des"
)

const (
    MYKEY = "abcdefgh" //八字节密匙
    IV    = "aaaabbbb" //CBC模式的初始化向量
)



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

    return dst
}

//使用des进行解密
func DecryptDES(src, key []byte) []byte {
    //1. 创建并返回一个使用DES算法的cipher.Block接口
    block, err := des.NewCipher(key)
    if err != nil {
        panic(err)
    }
    //2. 创建一个密码分组为链接模式的,底层使用DES解密的BlockMode接口
    cbcDecrypter := cipher.NewCBCDecrypter(block, []byte(IV))
    //3. 数据块解密
    dst := make([]byte, len(src))
    cbcDecrypter.CryptBlocks(dst, src)
    //4. 去掉最后一组填充数据
    newBytes := unPaddingBytes(dst)
    return newBytes
}

使用以上加解密方法:

//测试DES加解密算法
func TestDES() {
    fmt.Println("测试DES对称加密:")
    srcString := "GO 密码学 —— DES对称加密实现"
    //加密
    cryptBytes := myDES.EncryptDES([]byte(srcString), []byte(myDES.MYKEY))
    fmt.Println("加密效果:")
    fmt.Println("src :", hex.EncodeToString([]byte(srcString)), "[]byte长度:", len([]byte(srcString)))
    fmt.Println("dst :", hex.EncodeToString(cryptBytes), "[]byte长度:", len(cryptBytes))
    fmt.Println()

    //解密
    deCryptBytes := myDES.DecryptDES(cryptBytes, []byte(myDES.MYKEY))
    fmt.Println("解密效果:")
    fmt.Println("src :", hex.EncodeToString(cryptBytes), "[]byte长度:", len(cryptBytes))
    fmt.Println("dst :", hex.EncodeToString(deCryptBytes), "[]byte长度:", len(deCryptBytes))

    fmt.Println()
    fmt.Println("原字串为:" + srcString)
    fmt.Println("解密字符为:" + string(deCryptBytes))
}

//OUTPUT:
测试DES对称加密:
加密效果:
src : 474f20e5af86e7a081e5ada620e28094e2809420444553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte长度: 41
dst : e50636c0545eb031b3b36d363c050deb0640806475b04da4e5b54641eb635b1394d70f86c12f4119319904475235d441 []byte长度: 48

解密效果:
src : e50636c0545eb031b3b36d363c050deb0640806475b04da4e5b54641eb635b1394d70f86c12f4119319904475235d441 []byte长度: 48
dst : 474f20e5af86e7a081e5ada620e28094e2809420444553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte长度: 41

原字串为:GO 密码学 —— DES对称加密实现
解密字符为:GO 密码学 —— DES对称加密实现

以上就是Go 中DES的加解密实现,可见其加密的安全性依赖于秘钥的管理,而且其只有64Bit的秘钥长度在算力越来越高的现在已经不够安全了,这也是对称加密的弱点,在实际的网络项目中,秘钥的分发需要依赖其他方式的配合。

三、TDES(TripleDES)对称加密

对于DES秘钥较弱的问题做了一些改进,这就是三重DES加密算法,其原理与DES类似,唯一不同的是秘钥从一组八字节变成三组二十四字节(192Bit)。其加密强度也和秘钥设置相关,此处演示我们也用CBC分组模式,三重加密执行顺序分别为,加密->解密->加密。

为了兼容DES,只有当秘钥一组和秘钥二组相同时,3DES其加密安全性和DES相同,其余秘钥的分组设置都是三重DES加密。

import (
    "bytes"
    "crypto/cipher"
    "crypto/des"
)

const (
    MYKEY = "abcdefgh12345678ABCDEFGH" //三组八字节密匙,即24字节
    IV    = "aaaabbbb"                 //CBC模式的初始化向量,8字节
)

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

    return dst
}

//使用3des进行解密
func DecryptTDES(src, key []byte) []byte {
    //1. 创建并返回一个使用DES算法的cipher.Block接口
    block, err := des.NewTripleDESCipher(key)
    if err != nil {
        panic(err)
    }
    //2. 创建一个密码分组为链接模式的,底层使用DES解密的BlockMode接口
    cbcDecrypter := cipher.NewCBCDecrypter(block, []byte(IV))
    //3. 数据块解密
    dst := make([]byte, len(src))
    cbcDecrypter.CryptBlocks(dst, src)
    //4. 去掉最后一组填充数据,和DES的删除算法一样
    newBytes := unPaddingBytes(dst)
    return newBytes
}

使用以上加解密方法:

//测试三重DES对称加密算法
func TestTDES() {
    fmt.Println("测试TDES对称加密:")
    srcString := "GO 密码学 —— TDES对称加密实现"
    //加密
    cryptBytes := myTDES.EncryptTDES([]byte(srcString), []byte(myTDES.MYKEY))
    fmt.Println("加密效果:")
    fmt.Println("src :", hex.EncodeToString([]byte(srcString)), "[]byte长度:", len([]byte(srcString)))
    fmt.Println("dst :", hex.EncodeToString(cryptBytes), "[]byte长度:", len(cryptBytes))
    fmt.Println()

    //解密
    deCryptBytes := myTDES.DecryptTDES(cryptBytes, []byte(my3DES.MYKEY))
    fmt.Println("解密效果:")
    fmt.Println("src bytes:", hex.EncodeToString(cryptBytes), "[]byte长度:", len(cryptBytes))
    fmt.Println("dst bytes:", hex.EncodeToString(deCryptBytes), "[]byte长度:", len(deCryptBytes))

    fmt.Println()
    fmt.Println("原字串为:" + srcString)
    fmt.Println("解密字符为:" + string(deCryptBytes))
}

//OUTPUT:
测试TDES对称加密:
加密效果:
src : 474f20e5af86e7a081e5ada620e28094e280942033444553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte长度: 42
dst : 41da5be3a5b9f05438c2896173d141f43c7b12168e80ef299b35a46b76799ab0d3c573f1fc544a6ec3a135f4d5b912ba []byte长度: 48

解密效果:
src bytes: 41da5be3a5b9f05438c2896173d141f43c7b12168e80ef299b35a46b76799ab0d3c573f1fc544a6ec3a135f4d5b912ba []byte长度: 48
dst bytes: 474f20e5af86e7a081e5ada620e28094e280942033444553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte长度: 42

原字串为:GO 密码学 —— TDES对称加密实现
解密字符为:GO 密码学 —— TDES对称加密实现

四、AES对称加密

随着时代的发展,DES和3DES于现今的算力和性能要求来说已经逐渐落伍,为了适应更高的安全性和性能需要,业界采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。这就是AES对称加密算法,在现今的项目开发中,建议使用这种加密方式。

AES的基本要求是,采用对称分组密码体制,密钥的长度最少支持为128、192、256,分组长度128位。

所以要实现AES加密需要准备如下要求:

  • ==准备一个十六字节(128Bit)的秘钥==;
  • 原文需要按十六字节(128Bit)长度进行分组,采用常用的CBC(密码分组链接模式),准备十六字节(128Bit)长度的初始化向量值;
  • ==实现加密时的未位分组填充算法和解密时的删除密文末尾分组算法。这里的填充和删除算法和DES相同,不再赘述==

Go通过crypto/aes包支持DES加密算法:


import (
    "bytes"
    "crypto/aes"
    "crypto/cipher"
)

const (
    MYKEY = "abcdefgh12345678" //十六字节密匙
    IV    = "aaaabbbb12345678" //CBC模式的初始化向量:与key等长:十六字节
)



//使用aes进行对称加密
func EncryptAES(src, key []byte) []byte {
    //1. 创建并返回一个使用DES算法的cipher.Block接口
    block, err := aes.NewCipher([]byte(key))
    if err != nil {
        panic(err)
    }
    //2. 对最后一个明文分组进行数据填充
    src = paddingBytes(src, block.BlockSize())
    //3. 创建一个密码分组为链接模式的,底层使用DES加密的BlockMode接口
    cbcDecrypter := cipher.NewCBCEncrypter(block, []byte(IV))
    //4. 加密连续的数据并返回
    dst := make([]byte, len(src))
    cbcDecrypter.CryptBlocks(dst, src)

    return dst
}

//使用aes进行解密
func DecryptAES(src, key []byte) []byte {
    //1. 创建并返回一个使用DES算法的cipher.Block接口
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }
    //2. 创建一个密码分组为链接模式的,底层使用DES解密的BlockMode接口
    cbcDecrypter := cipher.NewCBCDecrypter(block, []byte(IV))
    //3. 数据块解密
    dst := make([]byte, len(src))
    cbcDecrypter.CryptBlocks(dst, src)
    //4. 去掉最后一组填充数据
    newBytes := unPaddingBytes(dst)
    return newBytes
}

以上,其用法和DES差不多,只不过秘钥管理稍微不同而已。

//测试AES对称加密算法
func TestAES() {
    fmt.Println("测试AES对称加密:")
    srcString := "GO 密码学 —— AES对称加密实现"
    //加密
    cryptBytes := myAES.EncryptAES([]byte(srcString), []byte(myAES.MYKEY))
    fmt.Println("加密效果:")
    fmt.Println("src :", hex.EncodeToString([]byte(srcString)), "[]byte长度:", len([]byte(srcString)))
    fmt.Println("dst :", hex.EncodeToString(cryptBytes), "[]byte长度:", len(cryptBytes))
    fmt.Println()

    //解密
    deCryptBytes := myAES.DecryptAES(cryptBytes, []byte(myAES.MYKEY))
    fmt.Println("解密效果:")
    fmt.Println("src bytes:", hex.EncodeToString(cryptBytes), "[]byte长度:", len(cryptBytes))
    fmt.Println("dst bytes:", hex.EncodeToString(deCryptBytes), "[]byte长度:", len(deCryptBytes))

    fmt.Println()
    fmt.Println("原字串为:" + srcString)
    fmt.Println("解密字符为:" + string(deCryptBytes))
}

//OUTPUT:
测试AES对称加密:
加密效果:
src : 474f20e5af86e7a081e5ada620e28094e2809420414553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte长度: 41
dst : 07a22e3059c8e9da2a3c1db2801b478a18f3ca7b33ace2f19931d2100a792681c61ac7ff3a61e0973be91638b3e9b1e1 []byte长度: 48

解密效果:
src bytes: 07a22e3059c8e9da2a3c1db2801b478a18f3ca7b33ace2f19931d2100a792681c61ac7ff3a61e0973be91638b3e9b1e1 []byte长度: 48
dst bytes: 474f20e5af86e7a081e5ada620e28094e2809420414553e5afb9e7a7b0e58aa0e5af86e5ae9ee78eb0 []byte长度: 41

原字串为:GO 密码学 —— AES对称加密实现
解密字符为:GO 密码学 —— AES对称加密实现

你可能感兴趣的:(6 Go 密码学(三)对称加密 DES、TDES、AES)