tyc-server/pkg/lzkit/crypto/ecb.go

275 lines
7.1 KiB
Go
Raw Permalink Normal View History

2025-04-09 17:27:40 +08:00
package crypto
import (
"crypto/aes"
"crypto/md5"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
)
// ECB模式是一种基本的加密模式每个明文块独立加密
// 警告ECB模式存在安全问题仅用于需要确定性加密的场景如数据库字段查询
// 不要用于加密大段文本或安全要求高的场景
// 验证密钥长度是否有效 (AES-128, AES-192, AES-256)
func validateAESKey(key []byte) error {
switch len(key) {
case 16, 24, 32:
return nil
default:
return errors.New("AES密钥长度必须是16、24或32字节(对应AES-128、AES-192、AES-256)")
}
}
// AesEcbEncrypt AES-ECB模式加密返回Base64编码的密文
// 使用已有的ECB实现但提供更易用的接口
func AesEcbEncrypt(plainText, key []byte) (string, error) {
if err := validateAESKey(key); err != nil {
return "", err
}
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
// 使用PKCS7填充
plainText = PKCS7Padding(plainText, block.BlockSize())
// 创建密文数组
cipherText := make([]byte, len(plainText))
// ECB模式加密使用west_crypto.go中已有的实现
mode := newECBEncrypter(block)
mode.CryptBlocks(cipherText, plainText)
// 返回Base64编码的密文
return base64.StdEncoding.EncodeToString(cipherText), nil
}
// AesEcbDecrypt AES-ECB模式解密输入Base64编码的密文
func AesEcbDecrypt(cipherTextBase64 string, key []byte) ([]byte, error) {
if err := validateAESKey(key); err != nil {
return nil, err
}
// Base64解码
cipherText, err := base64.StdEncoding.DecodeString(cipherTextBase64)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 检查密文长度
if len(cipherText)%block.BlockSize() != 0 {
return nil, errors.New("密文长度必须是块大小的整数倍")
}
// 创建明文数组
plainText := make([]byte, len(cipherText))
// ECB模式解密使用west_crypto.go中已有的实现
mode := newECBDecrypter(block)
mode.CryptBlocks(plainText, cipherText)
// 去除PKCS7填充
plainText, err = PKCS7UnPadding(plainText)
if err != nil {
return nil, err
}
return plainText, nil
}
// AesEcbEncryptHex AES-ECB模式加密返回十六进制编码的密文
func AesEcbEncryptHex(plainText, key []byte) (string, error) {
if err := validateAESKey(key); err != nil {
return "", err
}
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
// 使用PKCS7填充
plainText = PKCS7Padding(plainText, block.BlockSize())
// 创建密文数组
cipherText := make([]byte, len(plainText))
// ECB模式加密
mode := newECBEncrypter(block)
mode.CryptBlocks(cipherText, plainText)
// 返回十六进制编码的密文
return hex.EncodeToString(cipherText), nil
}
// AesEcbDecryptHex AES-ECB模式解密输入十六进制编码的密文
func AesEcbDecryptHex(cipherTextHex string, key []byte) ([]byte, error) {
if err := validateAESKey(key); err != nil {
return nil, err
}
// 十六进制解码
cipherText, err := hex.DecodeString(cipherTextHex)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 检查密文长度
if len(cipherText)%block.BlockSize() != 0 {
return nil, errors.New("密文长度必须是块大小的整数倍")
}
// 创建明文数组
plainText := make([]byte, len(cipherText))
// ECB模式解密
mode := newECBDecrypter(block)
mode.CryptBlocks(plainText, cipherText)
// 去除PKCS7填充
plainText, err = PKCS7UnPadding(plainText)
if err != nil {
return nil, err
}
return plainText, nil
}
// 以下是专门用于处理手机号等敏感数据的实用函数
// EncryptMobile 使用AES-ECB加密手机号返回Base64编码
// 该方法保证对相同手机号总是产生相同密文,便于数据库查询
func EncryptMobile(mobile string, secretKey string) (string, error) {
key, decodeErr := hex.DecodeString(secretKey)
if decodeErr != nil {
return "", decodeErr
}
if mobile == "" {
return "", errors.New("手机号不能为空")
}
return AesEcbEncrypt([]byte(mobile), key)
}
// DecryptMobile 解密手机号
func DecryptMobile(encryptedMobile string, secretKey string) (string, error) {
key, decodeErr := hex.DecodeString(secretKey)
if decodeErr != nil {
return "", decodeErr
}
if encryptedMobile == "" {
return "", errors.New("加密手机号不能为空")
}
bytes, err := AesEcbDecrypt(encryptedMobile, key)
if err != nil {
return "", fmt.Errorf("解密手机号失败: %v", err)
}
return string(bytes), nil
}
// EncryptMobileHex 使用AES-ECB加密手机号返回十六进制编码(适用于URL参数)
func EncryptMobileHex(mobile string, key []byte) (string, error) {
if mobile == "" {
return "", errors.New("手机号不能为空")
}
return AesEcbEncryptHex([]byte(mobile), key)
}
// DecryptMobileHex 解密十六进制编码的手机号
func DecryptMobileHex(encryptedMobileHex string, key []byte) (string, error) {
if encryptedMobileHex == "" {
return "", errors.New("加密手机号不能为空")
}
bytes, err := AesEcbDecryptHex(encryptedMobileHex, key)
if err != nil {
return "", fmt.Errorf("解密手机号失败: %v", err)
}
return string(bytes), nil
}
// EncryptIDCard 使用AES-ECB加密身份证号
func EncryptIDCard(idCard string, key []byte) (string, error) {
if idCard == "" {
return "", errors.New("身份证号不能为空")
}
return AesEcbEncrypt([]byte(idCard), key)
}
// DecryptIDCard 解密身份证号
func DecryptIDCard(encryptedIDCard string, key []byte) (string, error) {
if encryptedIDCard == "" {
return "", errors.New("加密身份证号不能为空")
}
bytes, err := AesEcbDecrypt(encryptedIDCard, key)
if err != nil {
return "", fmt.Errorf("解密身份证号失败: %v", err)
}
return string(bytes), nil
}
// IsEncrypted 检查字符串是否为Base64编码的加密数据
func IsEncrypted(data string) bool {
// 检查是否是有效的Base64编码
_, err := base64.StdEncoding.DecodeString(data)
return err == nil && len(data) >= 20 // 至少20个字符的Base64字符串
}
// GenerateAESKey 生成AES密钥
// keySize: 可选16, 24, 32字节(对应AES-128, AES-192, AES-256)
func GenerateAESKey(keySize int) ([]byte, error) {
if keySize != 16 && keySize != 24 && keySize != 32 {
return nil, errors.New("密钥长度必须是16、24或32字节")
}
key := make([]byte, keySize)
_, err := rand.Read(key)
if err != nil {
return nil, err
}
return key, nil
}
// DeriveKeyFromPassword 基于密码派生固定长度的AES密钥
func DeriveKeyFromPassword(password string, keySize int) ([]byte, error) {
if keySize != 16 && keySize != 24 && keySize != 32 {
return nil, errors.New("密钥长度必须是16、24或32字节")
}
// 使用PBKDF2或简单的方法从密码派生密钥
// 这里使用简单的MD5方法实际生产环境应使用更安全的PBKDF2
hash := md5.New()
hash.Write([]byte(password))
key := hash.Sum(nil) // 16字节
// 如果需要24或32字节继续哈希
if keySize > 16 {
hash.Reset()
hash.Write(key)
key = append(key, hash.Sum(nil)[:keySize-16]...)
}
return key, nil
}