qnc-server-tob/pkg/lzkit/md5/md5.go

207 lines
4.2 KiB
Go
Raw Normal View History

2025-04-08 12:49:19 +08:00
package md5
import (
"bufio"
"crypto/md5"
"encoding/hex"
"io"
"os"
"strings"
)
// MD5结构体可用于链式调用
type MD5 struct {
data []byte
}
// New 创建一个新的MD5实例
func New() *MD5 {
return &MD5{
data: []byte{},
}
}
// FromString 从字符串创建MD5
func FromString(s string) *MD5 {
return &MD5{
data: []byte(s),
}
}
// FromBytes 从字节切片创建MD5
func FromBytes(b []byte) *MD5 {
return &MD5{
data: b,
}
}
// Add 向MD5中添加字符串
func (m *MD5) Add(s string) *MD5 {
m.data = append(m.data, []byte(s)...)
return m
}
// AddBytes 向MD5中添加字节切片
func (m *MD5) AddBytes(b []byte) *MD5 {
m.data = append(m.data, b...)
return m
}
// Sum 计算并返回MD5哈希值(16进制字符串)
func (m *MD5) Sum() string {
hash := md5.New()
hash.Write(m.data)
return hex.EncodeToString(hash.Sum(nil))
}
// SumBytes 计算并返回MD5哈希值(字节切片)
func (m *MD5) SumBytes() []byte {
hash := md5.New()
hash.Write(m.data)
return hash.Sum(nil)
}
// 直接调用的工具函数
// EncryptString 加密字符串
func EncryptString(s string) string {
hash := md5.New()
hash.Write([]byte(s))
return hex.EncodeToString(hash.Sum(nil))
}
// EncryptBytes 加密字节切片
func EncryptBytes(b []byte) string {
hash := md5.New()
hash.Write(b)
return hex.EncodeToString(hash.Sum(nil))
}
// EncryptFile 加密文件内容
func EncryptFile(filePath string) (string, error) {
file, err := os.Open(filePath)
if err != nil {
return "", err
}
defer file.Close()
hash := md5.New()
if _, err := io.Copy(hash, file); err != nil {
return "", err
}
return hex.EncodeToString(hash.Sum(nil)), nil
}
// EncryptFileChunk 对大文件分块计算MD5提高效率
func EncryptFileChunk(filePath string, chunkSize int) (string, error) {
if chunkSize <= 0 {
chunkSize = 1024 * 1024 // 默认1MB
}
file, err := os.Open(filePath)
if err != nil {
return "", err
}
defer file.Close()
hash := md5.New()
buf := make([]byte, chunkSize)
reader := bufio.NewReader(file)
for {
n, err := reader.Read(buf)
if err != nil && err != io.EOF {
return "", err
}
if n == 0 {
break
}
hash.Write(buf[:n])
}
return hex.EncodeToString(hash.Sum(nil)), nil
}
// EncryptStringWithSalt 使用盐值加密字符串
func EncryptStringWithSalt(s, salt string) string {
return EncryptString(s + salt)
}
// EncryptStringWithPrefix 使用前缀加密字符串
func EncryptStringWithPrefix(s, prefix string) string {
return EncryptString(prefix + s)
}
// VerifyMD5 验证字符串的MD5哈希是否匹配
func VerifyMD5(s, hash string) bool {
return EncryptString(s) == strings.ToLower(hash)
}
// VerifyMD5WithSalt 验证带盐值的字符串MD5哈希是否匹配
func VerifyMD5WithSalt(s, salt, hash string) bool {
return EncryptStringWithSalt(s, salt) == strings.ToLower(hash)
}
// VerifyFileMD5 验证文件的MD5哈希是否匹配
func VerifyFileMD5(filePath, hash string) (bool, error) {
fileHash, err := EncryptFile(filePath)
if err != nil {
return false, err
}
return fileHash == strings.ToLower(hash), nil
}
// MD5格式化为指定位数
// Get16 获取16位MD5值(取32位结果的中间16位)
func Get16(s string) string {
result := EncryptString(s)
return result[8:24]
}
// Get8 获取8位MD5值
func Get8(s string) string {
result := EncryptString(s)
return result[12:20]
}
// MD5主要用于校验而非安全存储对于需要高安全性的场景应考虑:
// 1. bcrypt, scrypt或Argon2等专门为密码设计的算法
// 2. HMAC-MD5等方式以防御彩虹表攻击
// 3. 加盐并使用多次哈希迭代提高安全性
// MD5HMAC 使用HMAC-MD5算法
func MD5HMAC(message, key string) string {
hash := md5.New()
// 如果key长度超出block size先进行哈希
if len(key) > 64 {
hash.Write([]byte(key))
key = hex.EncodeToString(hash.Sum(nil))
hash.Reset()
}
// 内部填充
k_ipad := make([]byte, 64)
k_opad := make([]byte, 64)
copy(k_ipad, []byte(key))
copy(k_opad, []byte(key))
for i := 0; i < 64; i++ {
k_ipad[i] ^= 0x36
k_opad[i] ^= 0x5c
}
// 内部哈希
hash.Write(k_ipad)
hash.Write([]byte(message))
innerHash := hash.Sum(nil)
hash.Reset()
// 外部哈希
hash.Write(k_opad)
hash.Write(innerHash)
return hex.EncodeToString(hash.Sum(nil))
}