Golang AES 128/192/256 CBC 加密解密

yufei       5 年, 6 月 前       11343

今天修改一个项目的时候需要用到 AES 加密。为此重新学习了下 Go 中的以下两个包

crypto/aes
crypto/cipher

然后从网上随便抄了一段代码依葫芦画瓢写了一下代码,如下

package main

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

func PKCS7Padding(ciphertext []byte) []byte {
   padding := aes.BlockSize - len(ciphertext) % aes.BlockSize
   padtext := bytes.Repeat([]byte{byte(padding)}, padding)
   return append(ciphertext, padtext...)
}

func PKCS7UnPadding(plantText []byte) []byte {
   length   := len(plantText)
   unpadding := int(plantText[length-1])
   return plantText[:(length - unpadding)]
}


func main() {

    key, _ := hex.DecodeString("6368616e676520746869732070617373")
    iv,  _ := hex.DecodeString("3b9e61ed65ec555f43f9fcb41d5dde3a")
    plaintext := []byte("我是 password ")

    // -------- 加密开始---------

    plaintext = PKCS7Padding(plaintext)
    ciphertext := make([]byte,len(plaintext))   
    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }

    mode := cipher.NewCBCEncrypter(block, iv)
    mode.CryptBlocks(ciphertext, plaintext)
    fmt.Printf("%x\n", ciphertext)


    // ----------------解密开始---------

    mode = cipher.NewCBCDecrypter(block, iv)
    mode.CryptBlocks(ciphertext, ciphertext)

    ciphertext = PKCS7UnPadding(ciphertext)
    fmt.Printf("%s\n", ciphertext)
}

运行结果如下

go run main.go
711164f97fa824d7695698b73ee5dc4f97e64dd67106b47d8e50a6a388e55ea3
我是 password 

PKCS7PaddingPKCS5Padding 的区别

关于 PKCS7PaddingPKCS5Padding 的区别,从某些方面说是没区别的,因为它们的填充算法都一样

value = K - (L mod K)
K=块大小
L=数据长度

比如,如果 L=8, 则需要填充额外的 8个 byte 的 8

说起来唯一的区别就是:PKCS5PaddingPKCS7Padding 的子集

因为在 PKCS5Padding 中,明确定义 BlockSize 的大小(也就是 K )是 8 位。而在 PKCS7Padding 定义中,对于块的大小是不确定的,可以在 1-255之间(块长度超出 255的尚待研究),但一般都设置为 AES 块的默认大小,也就是 16 这样子。

const BlockSize = 16

16 指的是 byte 的大小,如果换成十六进制则是 32,一个 byte = 2 个 hex 字符

填充值的算法都是一样的:

iv

iv 被称为 初始化向量,如果你不理解它也没关系,你只要知道,iv 不同,即使加密密钥相同得出的结果也是不同的。

为了防止密钥被猜出来,我们可以随机化 iv

iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
    panic(err)
}

然后,我们可以把 iv 和加密后的数据一起返回给客户端

package main

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/rand"
    "encoding/hex"
    "bytes"
    "fmt"
    "io"
)

func PKCS7Padding(ciphertext []byte) []byte {
   padding := aes.BlockSize - len(ciphertext) % aes.BlockSize
   padtext := bytes.Repeat([]byte{byte(padding)}, padding)
   return append(ciphertext, padtext...)
}

func PKCS7UnPadding(plantText []byte) []byte {
   length   := len(plantText)
   unpadding := int(plantText[length-1])
   return plantText[:(length - unpadding)]
}


func main() {

    key, _ := hex.DecodeString("6368616e676520746869732070617373")

    plaintext := []byte("我是 password ")

    // ----------------加密开始---------

    plaintext = PKCS7Padding(plaintext)

    // 多申请 aes.BlockSize 长度用于存储 iv
    ciphertext := make([]byte,aes.BlockSize + len(plaintext))   

    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
        panic(err)
    }

    fmt.Printf("iv:%x\n",iv)


    block, err := aes.NewCipher(key)
    if err != nil {
        panic(err)
    }

    mode := cipher.NewCBCEncrypter(block, iv)
    // 注意偏移 aes.BlockSize
    mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
    hexciphertext := hex.EncodeToString(ciphertext)
    fmt.Printf("%s\n", hexciphertext)


    // ----------------解密开始---------
    byteciphertext, _ := hex.DecodeString(hexciphertext)
    deciphertext := make([]byte,len(hexciphertext))
    iv = byteciphertext[0:aes.BlockSize]
    mode = cipher.NewCBCDecrypter(block, iv)
    mode.CryptBlocks(deciphertext, byteciphertext[aes.BlockSize:])

    deciphertext = PKCS7UnPadding(deciphertext)
    fmt.Printf("%s\n", deciphertext)
}

运行结果如下

iv:cf29f14fad6e10c098fe8c808b4bceae
cf29f14fad6e10c098fe8c808b4bceae13a240b03ae254232d2d9b1a1837f762517be655462fd4f6f67d6d1719d4abdd
我是 password 

可以看到,每次运行的结果都不一样,这是因为 iv 不一样


后记:其实还有另外两种填充方式 NoPaddingZeroPadding

前者是不填充,什么数据就是什么数据,后者则是在 不不组 BlockSize 时填充 0 ,注意,这个 0

不过,为了兼容 JAVA,请谨慎选择填充方式,如果没有特别理由,请选择 PKCS5Padding

目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.