191 lines
4.7 KiB
Go
191 lines
4.7 KiB
Go
package common
|
||
|
||
import (
|
||
"bytes"
|
||
"crypto/cipher"
|
||
"crypto/des"
|
||
"crypto/md5"
|
||
"crypto/rand"
|
||
"encoding/hex"
|
||
"errors"
|
||
"fmt"
|
||
)
|
||
|
||
// PasswordUtil 对应 Java 类的常量
|
||
const (
|
||
// DefaultSalt 对应 Java 中的 SALT = "63293188"
|
||
DefaultSalt = "63293188"
|
||
// IterationCount 对应 Java 中的 ITERATIONCOUNT = 1000
|
||
IterationCount = 1000
|
||
)
|
||
|
||
// GetSalt 生成 8 字节的随机盐
|
||
func GetSalt() ([]byte, error) {
|
||
salt := make([]byte, 8)
|
||
_, err := rand.Read(salt)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return salt, nil
|
||
}
|
||
|
||
// GetStaticSalt 获取静态盐
|
||
func GetStaticSalt() []byte {
|
||
return []byte(DefaultSalt)
|
||
}
|
||
|
||
// Encrypt 加密
|
||
// plaintext: 明文
|
||
// password: 密码
|
||
// salt: 盐 (传入 string, 对应 Java 的 salt 参数)
|
||
func Encrypt(plaintext, password, salt string) (string, error) {
|
||
// 1. 生成 Key 和 IV
|
||
key, iv := deriveKeyAndIV(password, salt, IterationCount)
|
||
|
||
// 2. 创建 DES Cipher
|
||
block, err := des.NewCipher(key)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
// 3. 处理数据 (UTF-8 转 bytes 并填充)
|
||
data := []byte(plaintext)
|
||
data = pkcs5Padding(data, block.BlockSize())
|
||
|
||
// 4. 加密 (CBC 模式)
|
||
blockMode := cipher.NewCBCEncrypter(block, iv)
|
||
crypted := make([]byte, len(data))
|
||
blockMode.CryptBlocks(crypted, data)
|
||
|
||
// 5. 转十六进制字符串 (对应 Java 的 bytesToHexString)
|
||
// Java 的 Integer.toHexString 产生的是小写,这里保持一致,
|
||
// 但通常十六进制可以互通。
|
||
return hex.EncodeToString(crypted), nil
|
||
}
|
||
|
||
// Decrypt 解密
|
||
// ciphertext: 密文 (Hex 字符串)
|
||
// password: 密码
|
||
// salt: 盐
|
||
func Decrypt(ciphertext, password, salt string) (string, error) {
|
||
// 1. 生成 Key 和 IV
|
||
key, iv := deriveKeyAndIV(password, salt, IterationCount)
|
||
|
||
// 2. Hex 字符串转 bytes
|
||
decodedBytes, err := hex.DecodeString(ciphertext)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
// 3. 创建 DES Cipher
|
||
block, err := des.NewCipher(key)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
// 4. 解密 (CBC 模式)
|
||
blockMode := cipher.NewCBCDecrypter(block, iv)
|
||
// 密文长度检查
|
||
if len(decodedBytes)%block.BlockSize() != 0 {
|
||
return "", errors.New("ciphertext is not a multiple of the block size")
|
||
}
|
||
origData := make([]byte, len(decodedBytes))
|
||
blockMode.CryptBlocks(origData, decodedBytes)
|
||
|
||
// 5. 去除填充
|
||
origData, err = pkcs5UnPadding(origData)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
return string(origData), nil
|
||
}
|
||
|
||
// deriveKeyAndIV 模拟 Java PBEWithMD5AndDES 的密钥派生逻辑
|
||
// 逻辑:MD5(password || salt) -> 迭代 -> 结果前8字节是Key,后8字节是IV
|
||
func deriveKeyAndIV(password, salt string, iterations int) ([]byte, []byte) {
|
||
// Java PBEKeySpec 处理 password 为 char[],通常转 byte 时依赖编码,
|
||
// 此处假设使用 UTF-8 兼容(Java 默认行为通常如此,或者 ASCII)
|
||
passBytes := []byte(password)
|
||
saltBytes := []byte(salt)
|
||
|
||
// 第一次迭代: Hash(pass + salt)
|
||
hash := md5.New()
|
||
hash.Write(passBytes)
|
||
hash.Write(saltBytes)
|
||
derived := hash.Sum(nil)
|
||
|
||
// 后续迭代: Hash(prev_hash)
|
||
for i := 1; i < iterations; i++ {
|
||
hash.Reset()
|
||
hash.Write(derived)
|
||
derived = hash.Sum(nil)
|
||
}
|
||
|
||
// MD5 结果是 16 字节
|
||
// DES Key 需要 8 字节,IV 需要 8 字节
|
||
// 正好平分
|
||
key := derived[:8]
|
||
iv := derived[8:]
|
||
|
||
return key, iv
|
||
}
|
||
|
||
// pkcs5Padding 填充
|
||
func pkcs5Padding(ciphertext []byte, blockSize int) []byte {
|
||
padding := blockSize - len(ciphertext)%blockSize
|
||
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||
return append(ciphertext, padtext...)
|
||
}
|
||
|
||
// pkcs5UnPadding 去除填充
|
||
func pkcs5UnPadding(origData []byte) ([]byte, error) {
|
||
length := len(origData)
|
||
if length == 0 {
|
||
return nil, errors.New("decryption error: output length is zero")
|
||
}
|
||
unpadding := int(origData[length-1])
|
||
if length < unpadding {
|
||
return nil, errors.New("decryption error: invalid padding")
|
||
}
|
||
return origData[:(length - unpadding)], nil
|
||
}
|
||
|
||
// --- 测试主函数 ---
|
||
func main() {
|
||
// 测试数据
|
||
plaintext := "admin"
|
||
password := "Wang5322570"
|
||
//salt := DefaultSalt
|
||
salt := "RCGTeGiH"
|
||
|
||
fmt.Println("原文:", plaintext)
|
||
fmt.Println("密码:", password)
|
||
fmt.Println("盐值:", salt)
|
||
|
||
// 加密
|
||
encrypted, err := Encrypt(plaintext, password, salt)
|
||
if err != nil {
|
||
fmt.Println("加密失败:", err)
|
||
return
|
||
}
|
||
// Java代码可能输出小写或手动处理,这里使用 hex.EncodeToString (小写)
|
||
// 如果需要大写可以使用 strings.ToUpper(encrypted)
|
||
fmt.Println("加密密文 (Hex):", encrypted)
|
||
|
||
// 解密
|
||
decrypted, err := Decrypt(encrypted, password, salt)
|
||
if err != nil {
|
||
fmt.Println("解密失败:", err)
|
||
return
|
||
}
|
||
fmt.Println("解密原文:", decrypted)
|
||
|
||
// 验证一致性
|
||
if plaintext == decrypted {
|
||
fmt.Println(">> 测试通过!Go版本与逻辑一致。")
|
||
} else {
|
||
fmt.Println(">> 测试失败!")
|
||
}
|
||
}
|