86 lines
2.1 KiB
Go
86 lines
2.1 KiB
Go
package cryptox
|
||
|
||
import (
|
||
"crypto/aes"
|
||
"crypto/cipher"
|
||
"crypto/md5"
|
||
"crypto/sha256"
|
||
"encoding/base64"
|
||
"encoding/hex"
|
||
"fmt"
|
||
"time"
|
||
)
|
||
|
||
// generateKey: SHA256(secret)[:32]
|
||
func generateKey(secret string) []byte {
|
||
h := sha256.Sum256([]byte(secret))
|
||
return h[:32]
|
||
}
|
||
|
||
// generateIv: SHA256(hex(md5(nonce)) + secret)[:16]
|
||
func generateIv(nonce, secret string) []byte {
|
||
md5h := md5.Sum([]byte(nonce))
|
||
md5hex := hex.EncodeToString(md5h[:])
|
||
sha := sha256.Sum256([]byte(md5hex + secret))
|
||
return sha[:16]
|
||
}
|
||
|
||
// Encrypt 加密明文,返回 base64密文 和 nonce(hex(UnixNano))
|
||
func Encrypt(plaintext []byte, secret string) (dataB64 string, nonce string, err error) {
|
||
nonce = fmt.Sprintf("%x", time.Now().UnixNano())
|
||
key := generateKey(secret)
|
||
iv := generateIv(nonce, secret)
|
||
|
||
block, err := aes.NewCipher(key)
|
||
if err != nil {
|
||
return
|
||
}
|
||
padded := pkcs7Pad(plaintext, aes.BlockSize)
|
||
mode := cipher.NewCBCEncrypter(block, iv)
|
||
dst := make([]byte, len(padded))
|
||
mode.CryptBlocks(dst, padded)
|
||
dataB64 = base64.StdEncoding.EncodeToString(dst)
|
||
return
|
||
}
|
||
|
||
// Decrypt 解密,nonce 用于派生 IV
|
||
func Decrypt(dataB64, secret, nonce string) ([]byte, error) {
|
||
ciphertext, err := base64.StdEncoding.DecodeString(dataB64)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
key := generateKey(secret)
|
||
iv := generateIv(nonce, secret)
|
||
block, err := aes.NewCipher(key)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if len(ciphertext)%aes.BlockSize != 0 {
|
||
return nil, fmt.Errorf("ciphertext length not multiple of block size")
|
||
}
|
||
mode := cipher.NewCBCDecrypter(block, iv)
|
||
dst := make([]byte, len(ciphertext))
|
||
mode.CryptBlocks(dst, ciphertext)
|
||
return pkcs7Unpad(dst)
|
||
}
|
||
|
||
func pkcs7Pad(data []byte, blockSize int) []byte {
|
||
pad := blockSize - len(data)%blockSize
|
||
padding := make([]byte, pad)
|
||
for i := range padding {
|
||
padding[i] = byte(pad)
|
||
}
|
||
return append(data, padding...)
|
||
}
|
||
|
||
func pkcs7Unpad(data []byte) ([]byte, error) {
|
||
if len(data) == 0 {
|
||
return nil, fmt.Errorf("empty data")
|
||
}
|
||
pad := int(data[len(data)-1])
|
||
if pad == 0 || pad > aes.BlockSize {
|
||
return nil, fmt.Errorf("invalid padding size: %d", pad)
|
||
}
|
||
return data[:len(data)-pad], nil
|
||
}
|