golang-yitisheng-server/server/common/password.go

191 lines
4.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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(">> 测试失败!")
}
}