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