📌Golang📌应用📌加密算法📌2-ades.txt
"crypto/cipher"包提供了不同工作模式的核心算法,加解密得到密文或明文的字节数组。
密钥、明文、密文的字节数组通常使用Base64Std编码,也可以使用其他字节编码方式。

两种明文块填充方式的实现:
func Padding(src []byte, blockSize int) []byte {
	length := len(src)
	pad := blockSize - length%blockSize
	padText := make([]byte, pad-1) // AnsiX923补零填充
	//rand.Read(padText) // 变成Iso10126随机填充
	res := make([]byte, length, length+pad)
	copy(res, src)
	return append(res, padText...)
}

五种工作模式比较:
    密文长度:ECB=CBC>CFB=OFB=CTR
    内存占用:ECB<CFB<CBC<OFB=CTR
	加密耗时:ECB<CFB<CBC<CTR<OFB
建议:性能要求更高的场景(eg:日志实时加密)选用ECB/CFB模式,安全性要求更高的场景(eg:落库数据)选用CTR模式。

DES,3DES,AES有相似的初始化方法,相同的5种工作模式,可统一封装成通用方法。
DES密钥8字节,向量8字节。3DES密钥24字节,向量8字节。AES密钥16/24/32字节,向量16字节。
密文的编码方式通常采用标准的base64,但在某些场景也可以使用其他编码方式(hex,base64url等)。

========== ========== ========== ========== ==========

package ades

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/des"
	"encoding/base64"
	"errors"
)

// Cipher 一处实例化后可以多处并发安全复用
type Cipher struct {
	block cipher.Block
	iv    []byte
}

func NewDesCipher(key, iv []byte) (*Cipher, error) {
	return newCipher(des.NewCipher, key, iv)
}

func NewTripleDesCipher(key, iv []byte) (*Cipher, error) {
	return newCipher(des.NewTripleDESCipher, key, iv)
}

func NewAesCipher(key, iv []byte) (*Cipher, error) {
	return newCipher(aes.NewCipher, key, iv)
}

func newCipher(f func([]byte) (cipher.Block, error), key, iv []byte) (*Cipher, error) {
	block, err := f(key)
	if err != nil {
		return nil, err
	}
	ivb := make([]byte, block.BlockSize())
	copy(ivb, iv)
	return &Cipher{
		block: block,
		iv:    ivb,
	}, nil
}

func pkcs7Padding(src []byte, blockSize int) []byte {
	length := len(src)
	pad := blockSize - length%blockSize
	padText := bytes.Repeat([]byte{byte(pad)}, pad)
	res := make([]byte, length, length+pad)
	copy(res, src) //复制一份防止当src后cap足够的时候append修改其后的内容
	return append(res, padText...)
}

var ErrInvalidPadLen = errors.New("invalid padding length")

// 三种填充方式去除填充的方法都相同,根据最后一位来精确定位截取长度
func trimPadding(dst []byte) ([]byte, error) {
	length := len(dst)
	if length == 0 {
		return dst, nil
	}
	pad := int(dst[length-1])
	if length < pad {
		return nil, ErrInvalidPadLen
	}
	return dst[:length-pad], nil
}

func (c *Cipher) EncryptECB(src []byte) string {
	bs := c.block.BlockSize()
	src = pkcs7Padding(src, bs)
	length := len(src)
	dst := make([]byte, length)
	for i := 0; i < length; i += bs {
		c.block.Encrypt(dst[i:i+bs], src[i:i+bs])
	}
	return base64.StdEncoding.EncodeToString(dst)
}
func (c *Cipher) DecryptECB(s string) ([]byte, error) {
	src, err := base64.StdEncoding.DecodeString(s)
	if err != nil {
		return nil, err
	}
	bs := c.block.BlockSize()
	length := len(src)
	if length%bs != 0 {
		return nil, ErrInvalidPadLen
	}
	dst := make([]byte, length)
	for i := 0; i < length; i += bs {
		c.block.Decrypt(dst[i:i+bs], src[i:i+bs])
	}
	return trimPadding(dst)
}

func (c *Cipher) EncryptCBC(src []byte) string {
	src = pkcs7Padding(src, c.block.BlockSize())
	dst := make([]byte, len(src))
	bm := cipher.NewCBCEncrypter(c.block, c.iv)
	bm.CryptBlocks(dst, src)
	return base64.StdEncoding.EncodeToString(dst)
}
func (c *Cipher) DecryptCBC(s string) ([]byte, error) {
	src, err := base64.StdEncoding.DecodeString(s)
	if err != nil {
		return nil, err
	}
	dst := make([]byte, len(src))
	bm := cipher.NewCBCDecrypter(c.block, c.iv)
	bm.CryptBlocks(dst, src)
	return trimPadding(dst)
}

func (c *Cipher) EncryptCFB(src []byte) string {
	dst := make([]byte, len(src))
	stream := cipher.NewCFBEncrypter(c.block, c.iv)
	stream.XORKeyStream(dst, src)
	return base64.StdEncoding.EncodeToString(dst)
}
func (c *Cipher) DecryptCFB(s string) ([]byte, error) {
	src, err := base64.StdEncoding.DecodeString(s)
	if err != nil {
		return nil, err
	}
	dst := make([]byte, len(src))
	stream := cipher.NewCFBDecrypter(c.block, c.iv)
	stream.XORKeyStream(dst, src)
	return dst, nil
}

func (c *Cipher) EncryptOFB(src []byte) string {
	dst := make([]byte, len(src))
	stream := cipher.NewOFB(c.block, c.iv)
	stream.XORKeyStream(dst, src)
	return base64.StdEncoding.EncodeToString(dst)
}
func (c *Cipher) DecryptOFB(s string) ([]byte, error) {
	src, err := base64.StdEncoding.DecodeString(s)
	if err != nil {
		return nil, err
	}
	dst := make([]byte, len(src))
	stream := cipher.NewOFB(c.block, c.iv)
	stream.XORKeyStream(dst, src)
	return dst, nil
}

func (c *Cipher) EncryptCTR(src []byte) string {
	dst := make([]byte, len(src))
	stream := cipher.NewCTR(c.block, c.iv)
	stream.XORKeyStream(dst, src)
	return base64.StdEncoding.EncodeToString(dst)
}
func (c *Cipher) DecryptCTR(s string) ([]byte, error) {
	src, err := base64.StdEncoding.DecodeString(s)
	if err != nil {
		return nil, err
	}
	dst := make([]byte, len(src))
	stream := cipher.NewCTR(c.block, c.iv)
	stream.XORKeyStream(dst, src)
	return dst, nil
}