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
|
|||
|
}
|