一、MD5 算法特性
MD5 属于 Hash 算法中的一种,它具有以下特性:
- 输入任意长度的信息,经过处理,输出为 128 位的信息(数字指纹)。
- 不同的输入得到的不同的结果(唯一性)。
- 根据 128 位的输出结果不可能反推出输入的信息(不可逆)。
- 可见继承了 Hash 算法的优良特点,用处很多,如登录密码、数字签名等。
二、Go语言使用MD5算法
//方法一
func digest1(data []byte)([]byte,error){
h := md5.New()
_, err := h.Write(data)
if err != nil {
return nil,err
}
return h.Sum(nil),nil
}
//方法二
func digest2(data []byte)([]byte,error){
h := md5.Sum(data)
return h[:],nil
}
三、算法实现原理
MD5 是以 512 位分组来处理输入的信息,每一分组又被划分为 16 个 32 位子分组,经过了一系列的处理后,算法的输出由四个 32 位分组组成,将这四个 32 位分组拼接后生成一个 128 位 Hash 值,具体步骤如下:
- 填充:假如原始信息长度对 512 求余的结果不等于 448(这里说的单位是 bit,就是位,1 字节(Byte) = 8位(bit)),就需要填充使得对 512 求余的结果等于 448。填充的方法是填充一个 1 和 m 个 0。填充完后,信息的长度就为 n * 512 + 448(这里 n 表示的是 512 的整数倍)。
- 填充:假如原始信息长度对 512 求余的结果不等于 448(这里说的单位是 bit,就是位,1 字节(Byte) = 8位(bit)),就需要填充使得对 512 求余的结果等于 448。填充的方法是填充一个 1 和 m 个 0。填充完后,信息的长度就为 n * 512 + 448(这里 n 表示的是 512 的整数倍)。
- 记录长度:用 64 位来存储填充前信息长度,这 64 位加在第一步结果的后面,这样信息长度就变为 n * 512 + 448 + 64 = (n+1)*512位,也就是 512 的整数倍。
- 准备四个常数和四个函数:
- 4个常数:A = 0x67452301、B = 0x0EFCDAB89、C = 0x98BADCFE、D = 0x10325476;
- 4个函数:
F (X,Y,Z) = (X & Y) | ((~X) & Z) G (X,Y,Z) = (X & Z) | (Y & (~Z)) H (X,Y,Z) = X ^ Y ^ Z J (X,Y,Z) = Y ^ (X | (~Z))`
- 把消息分以 512 位为一分组进行处理,每一个分组进行 4 轮变换,以上面所说 4 个常数为起始变量进行计算,重新输出 4 个变量,以这 4 个变量再进行下一分组的运算,如果已经是最后一个分组,则这 4 个变量为最后的结果,即 MD5 值。
四、实现代码
package md5
import (
"crypto"
"hash"
)
func init() {
crypto.RegisterHash(crypto.MD5, New)
}
// The size of an MD5 checksum in bytes.
const Size = 16
// The blocksize of MD5 in bytes.
const BlockSize = 64
const (
chunk = 64
init0 = 0x67452301
init1 = 0xEFCDAB89
init2 = 0x98BADCFE
init3 = 0x10325476
)
// digest represents the partial evaluation of a checksum.
type digest struct {
s [4]uint32
x [chunk]byte
nx int
len uint64
}
func (d *digest) Reset() {
d.s[0] = init0
d.s[1] = init1
d.s[2] = init2
d.s[3] = init3
d.nx = 0
d.len = 0
}
// New returns a new hash.Hash computing the MD5 checksum.
func New() hash.Hash {
d := new(digest)
d.Reset()
return d
}
func (d *digest) Size() int { return Size }
func (d *digest) BlockSize() int { return BlockSize }
func (d *digest) Write(p []byte) (nn int, err error) {
nn = len(p)
d.len += uint64(nn)
if d.nx > 0 {
n := copy(d.x[d.nx:], p)
d.nx += n
if d.nx == chunk {
block(d, d.x[:])
d.nx = 0
}
p = p[n:]
}
if len(p) >= chunk {
n := len(p) &^ (chunk - 1)
block(d, p[:n])
p = p[n:]
}
if len(p) > 0 {
d.nx = copy(d.x[:], p)
}
return
}
func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing.
d := *d0
hash := d.checkSum()
return append(in, hash[:]...)
}
func (d *digest) checkSum() [Size]byte {
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
len := d.len
var tmp [64]byte
tmp[0] = 0x80
if len%64 < 56 {
d.Write(tmp[0 : 56-len%64])
} else {
d.Write(tmp[0 : 64+56-len%64])
}
// Length in bits.
len <<= 3
for i := uint(0); i < 8; i++ {
tmp[i] = byte(len >> (8 * i))
}
d.Write(tmp[0:8])
if d.nx != 0 {
panic("d.nx != 0")
}
var digest [Size]byte
for i, s := range d.s {
digest[i*4] = byte(s)
digest[i*4+1] = byte(s >> 8)
digest[i*4+2] = byte(s >> 16)
digest[i*4+3] = byte(s >> 24)
}
return digest
}
// Sum returns the MD5 checksum of the data.
func Sum(data []byte) [Size]byte {
var d digest
d.Reset()
d.Write(data)
return d.checkSum()
}