通过Golang编写一个AES加密解密工具

 更新时间:2022-05-13 14:10:33   作者:佚名   我要评论(0)

目录前言AES加密介绍及实现原理AES用在哪里AES加密是如何实现的AES加密模式Go实现AES加密工具scode前言
本文包含如下两个内容:
AES加密介绍

前言

本文包含如下两个内容:

AES加密介绍及实现原理

Go实现AES加密和解密工具

AES加密介绍及实现原理

AES( advanced encryption standard)使用相同密钥进行加密和解密,也就是对称加密。其他的对称加密如DES,由于DES密钥长度只有56位如今的算力甚至可以在5分钟内破解,而AES最高级别达到了256位密钥长度,如果采用穷举法,目前来看AES是一种”无法“被破解的加密存在。

AES用在哪里

如果你正在浏览本文,那么你就在使用AES(https协议中一部分使用了对称加密)。

  • 绿色上网:通过加密安全地连接到另一台搬石头砸脚的服务器。
  • 无线网络WIFI:和WAP2一起使用。
  • 应用程序:wechat、JD、Alipay等使用 AES 加密照片和消息或支付信息。
  • 存档和压缩工具:7z、WinZip 和 RAR。
  • 操作系统组件:一些操作系统组件(如文件系统)使用高级加密标准来确保安全性。
  • 编程语言库: Go、Python 和 C++ 等编码库实现了的AES加密(等会使用到)。

AES加密是如何实现的

参考:

what-is-the-aes-algorithm?

What is AES encryption and how does it work?

Block cipher mode of operation

从宏观上来看AES加密过程中的一轮(根据不同的密钥长度,轮数不一样,下面会说到)如下:

1.数据分块

首先把明文按照128bit拆分成若干个明文块(图上黄色块),一个字节包含 8 位,布局为 4×4矩阵(上图黄色部分),对最后一块填充至128bit,填充方式有PKCS7Padding(采用)/PKCS5Padding/ZeroPadding,无论咋填充最后解密时都要去除这些多余的填充。

2.密钥扩展

AES通过Rijndael's key schedule 将密钥被扩展为 (n+1) 个密钥,其中 n 是加密过程中要遵循的轮数。AES每个标准规定了所要加密的轮数,对于128位密钥,轮数是 10,要生成的密钥个数为 10+1,总共 11 个密钥。

标准密钥长度轮数分组长度
AES-128128位(16字节)10128位(16字节)
AES-192192位(24字节)12128位(16字节)
AES-256256位(32字节)14128位(16字节)

每一轮所要做的包括:字节替代(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)、加轮密钥(AddRoundKey)

3.字节替代(SubBytes)

每轮开始,首先进行SubBytes,字节根据预定义的 Rijndael S-box(可以简单认为是一个转换表)规定的规则进行替换。对a[i,j]中的每个字节进行一次转换后得到b[i,j]

4.行移位(ShiftRows)

对上一步得到矩阵进行ShiftRows,第一行不变,第二行移动1位,第三行2位,第四行3位。

5.列混淆(MixColumns)

再对矩阵的每一列和修补矩阵fixed matrix的二维常量数组做矩阵相乘,得到对应的输出列。

6.加轮密钥(AddRoundKey)

先将扩展密钥Kn排列成4×4矩阵,然后让输入数组的每一个字节a[i,j]与密钥对应位置的字节k[i,j]异或一次,得到输出b[i,j]。最后一轮不参与AddRoundKey

经过如上的10轮操作之后,得到了一个明文块的加密字符。解密则进行反向加密。

AES加密模式

ECB

在上面加密过程中每一个明文块都是独立进行加密的,简单且高效,但是如果一个段数据存在相关的明文块,则加密后的密文也会相同,对安全性也有一定影响。

CBC

CBC加密模式如下图所示,初始向量IV和明文异或,每个块的密文作为后续块的“向量”,让每一个密文独一无二。我们待会采用这种模式。

Go实现AES加密工具scode

ok,上面大致了解AES加密是如何工作起来的,接下来通过Go中的crypto/aes和crypto/cipher包实现的AES加密解密工具。

PKCS7Padding将待补足字节数作为填充的字节

// pkcs7Padding 填充
func pkcs7Padding(data []byte, blockSize int) []byte {
    //判断缺少几位长度。最少1,最多 blockSize
    padding := blockSize - len(data)%blockSize
    //补足位数。把切片[]byte{byte(padding)}复制padding个
    padText := bytes.Repeat([]byte{byte(padding)}, padding)
    return append(data, padText...)
}

// pkcs7UnPadding 移除
func pkcs7UnPadding(data []byte) ([]byte, error) {
    length := len(data)
    if length == 0 {
        return nil, errors.New("加密字符串错误!")
    }
    //获取填充的个数
    unPadding := int(data[length-1])
    return data[:(length - unPadding)], nil
}

使用 cipher的CBC模式对block加密和解密

// AesEncrypt 加密
func AesEncrypt(data []byte, key []byte) ([]byte, error) {    
    // NewCipher creates and returns a new cipher.Block. The key argument should be the AES key, either 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256.
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }
    //判断加密快的大小
    blockSize := block.BlockSize()
    //填充
    encryptBytes := pkcs7Padding(data, blockSize)
    //初始化加密数据接收切片
    crypted := make([]byte, len(encryptBytes))
    //使用cbc加密模式
    blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
    //执行加密
    blockMode.CryptBlocks(crypted, encryptBytes)
    return crypted, nil
}

// AesDecrypt 解密
func AesDecrypt(data []byte, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }
    //获取块的大小
    blockSize := block.BlockSize()
    //使用cbc
    blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
    //初始化解密数据接收切片
    crypted := make([]byte, len(data))
    //执行解密
    blockMode.CryptBlocks(crypted, data)
    //去填充
    crypted, err = pkcs7UnPadding(crypted)
    if err != nil {
        return nil, err
    }
    return crypted, nil
}

循环从文件中读取100mb源数据用于加密后将密文写入文件,解密则读取密文解密后将源数据写入文件。

func EncryptFile(fileName string) (err error) {
    f, err := os.Open(fileName)
    if err != nil {
        fmt.Println("未找到文件")
        return
    }
    defer f.Close()

    fInfo, _ := f.Stat()
    fLen := fInfo.Size()
    fmt.Println("待处理文件大小:", fLen)
    maxLen := 1024 * 1024 * 100 //100mb  每 100mb 进行加密一次
    var forNum int64 = 0
    getLen := fLen

    if fLen > int64(maxLen) {
        getLen = int64(maxLen)
        forNum = fLen / int64(maxLen)
        fmt.Println("需要加密次数:", forNum+1)
    }
    // encryptd to file
    ff, err := os.OpenFile("en_"+fileName, os.O_RDWR|os.O_CREATE, 0666)
    if err != nil {
        fmt.Println("文件写入错误")
        return err
    }
    defer ff.Close()
    //循环加密,并写入文件
    for i := 0; i < int(forNum+1); i++ {
        a := make([]byte, getLen)
        n, err := f.Read(a)
        if err != nil {
            fmt.Println("文件读取错误")
            return err
        }
        getByte, err := EncryptByAes(a[:n])
        if err != nil {
            fmt.Println("加密错误")
            return err
        }
        getBytes := append([]byte(getByte), []byte("\n")...)
        //写入
        buf := bufio.NewWriter(ff)
        buf.WriteString(string(getBytes[:]))
        buf.Flush()
    }
    ffInfo, _ := ff.Stat()
    fmt.Printf("加密后文件为:%s,文件大小为:%v Byte \n", ffInfo.Name(), ffInfo.Size())
    return nil
}

参考:Golang实现AES加密和解密的示例代码

通过cobra添加命令后,创建命令的匿名函数

func(cmd *cobra.Command, args []string) {
    copy(PwdKey, readPass())
    Pwd := []byte("csgo!gogo")
    if ByteSliceEqual(PwdKey, Pwd) {
        //16字节key
        PwdKey = append(PwdKey, 7, 3, 5, 5, 6, 0, 8)
        if err := DecryptFile(args[0]); err != nil {
            panic(err)
        }
    } else {
        fmt.Println("密码错误")
        os.Exit(1)
    }
}

使用方式看起来如下:

scode工具包含2个命令encode和decode,解密文件需要密码。

# ./scode  encode xpower.tar.gz
待处理文件大小: 3397
加密后文件为:en_xpower.tar.gz,文件大小为:4545 Byte

# ./scode  decode en_xpower.tar.gz
ENTER PASSWORD: 
密码错误

# ./scode  decode en_xpower.tar.gz
ENTER PASSWORD: 
待处理文件大小: 4545
解密后文件为:de_en_xpower.tar.gz,文件大小为:3159 Byte

完整代码:source

到此这篇关于通过Golang编写一个AES加密解密工具的文章就介绍到这了,更多相关Golang AES加密解密工具内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
  • go语言实现AES加密的方法
  • golang使用aes库实现加解密操作
  • Golang实现AES对称加密的过程详解
  • golang常用加密解密算法总结(AES、DES、RSA、Sha1、MD5)
  • Golang实现AES加密和解密的示例代码

相关文章

  • 图解AVL树数据结构输入与输出及实现示例

    图解AVL树数据结构输入与输出及实现示例

    目录AVL树(平衡二叉树):AVL树的作用:AVL树的基本操作:AVL树的插入,单旋转的第一种情况---右旋:AVL树的插入,单旋转的第二种情况---左旋
    2022-05-13
  • 通过Golang编写一个AES加密解密工具

    通过Golang编写一个AES加密解密工具

    目录前言AES加密介绍及实现原理AES用在哪里AES加密是如何实现的AES加密模式Go实现AES加密工具scode前言 本文包含如下两个内容: AES加密介绍
    2022-05-13
  • 深入理解Go语言实现多态?

    深入理解Go语言实现多态?

    目录多态是什么Go语言多态举例总结多态是什么 相信学过Java这种面向对象语言的同学对于多态来说都不陌生,在代码执行的时候,能够根据子类的
    2022-05-13
  • 300行代码实现go语言即时通讯聊天室

    300行代码实现go语言即时通讯聊天室

    学了2年Java,因为工作原因需要转Golang,3天时间学习了下go的基本语法,做这样一个聊天室小项目来巩固串联一下语法。 实现的功能:公聊,私
    2022-05-13
  • 分析讲解Java?Random类里的种子问题

    分析讲解Java?Random类里的种子问题

    可以说在现在的计算机语言里面,以及普通用户这里,都是没有办法获得真正的 随机数的。真正的随机数现在也只有在量子计算机当中才有获得。 所
    2022-05-13
  • networkx库绘制带权图给无权图加权重输出

    networkx库绘制带权图给无权图加权重输出

    目录问题输出结果如何只输出权重输出结果问题 最近在研究图学习,在用networkx库绘图的时候发现问题。 ''' author:zheng time:2020.10.23
    2022-05-13
  • SpringBoot整合Mongodb实现增删查改的方法

    SpringBoot整合Mongodb实现增删查改的方法

    目录一、什么是MongoDB二、在Window10上安装MongoDB三、配置MongoDB服务四、启动服务五、SpringBoot整合MongoDB一、什么是MongoDB MongoDB与
    2022-05-13
  • Java实现跳跃表的示例详解

    Java实现跳跃表的示例详解

    跳表全称叫做跳跃表,简称跳表,是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳表在原有的有序列表上面增加多级索引,
    2022-05-13
  • go语言心跳超时的实现示例

    go语言心跳超时的实现示例

    目录一、背景二、心跳超时的实现2.1 通过select case (设计概念比较多)2.2 通过time.sleep(简单有效)三、个人的实现观感一、背景 本文描
    2022-05-13
  • 利用Java编写一个属于自己的日历

    利用Java编写一个属于自己的日历

    目录问题提出一点提示源码分享问题提出 编写程序,输入年份,打印出该年的年历(12个月的),效果如下图所示。 一点提示 使用 Java 自带的
    2022-05-13

最新评论