275 lines
7.1 KiB
Go
275 lines
7.1 KiB
Go
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
|
||
}
|