用过PHP的童鞋知道在PHP里面md5很简单,是一个内置函数,可以直接调用:
1 | jwang@jun:~$ php -a |
纠正一个错误的说法,很多人一直把md5叫作加密算法,实际上md5并不是加密,它既不是对称加密,也不是非对称加密,它只是一个摘要函数,一般被用于签名或者校验数据完整性。
虽然现在有文章说不推荐使用md5了,因为碰撞几率比较大,实际上,这个几率非常非常非常低,大只是相对于其它摘要函数来说,纯自然的情况下基本不可能碰撞,虽然可以用工具构造出来,但非常复杂。如果实在不放心,可以用sha1或者sha256,或者两者集合起来用,速度会慢一点,但安全性高一点,总之,md5由于速度快,简单易用,现在用的还是蛮多的。
言归正传,在Go的标准库里面并没有md5这个函数,但是在crypto包里面确实有相关实现,需要自己动手组装拼凑一下,网上流传的写法有很多种:
1.第一种
1 | func MD5(s string) string { |
平时用到可能只是copy过来,没仔细看,今天来仔细看一下,首先,这个 md5.New() 返回的是一个结构体 digest:
这个结构体成员啥意思呢?其实细说起来,这和md5的算法有关了,咱也不知道,咱也不敢问!
但是仔细看一下这个结构体的方法,你会发现有一个叫Write,还有一个叫Sum,如果你英语不错,你可以看懂,Write就是把数据写到刚才这个结构体里面,先甭管它咋写,肯定是有算法规则,感兴趣可以研究研究。Sum稍微有点不一样,但是有一个参数,和一个返回值,这个方法的意思是把参数追加到进去并且返回摘要,由于我们之前已经写进去了,所以参数为nil即可。
可见,一个md5方法Go就整了6行代码,老板看你代码写这么多,又可以加薪了,Go确实是好语言。
2.第二种
如果你仔细看了这个包里面的 md5.go 文件,你会发现最下面有一个公开的方法Sum,仔细一看,这就是刚才我写的那个简化版:
1 | // Sum returns the MD5 checksum of the data. |
所以我们的方法可以简化为:
1 | func MD5(s string) string { |
当然sum变量在这2个方法里面都是多余的,可以简化为一行代码即可。
3.第三种
还有一种方式是使用io库的方法往里面写,主要是因为degest实现了io.Writer接口
1 | func MD5(s string) string { |
其中最后Sprintf方法是为了把结果转化成小写十六进制,也可以用hex.EncodeToString方法替代。
4.性能对比
这几种方式大同小异,理论上讲应该没有什么性能差距,不过既然Go自带Benchmark,我们就测一下吧:
1 | func BenchmarkMD5(b *testing.B) { |
结果如下:
1 | jwang@jun:~/Documents/Work/learnGo/Std/md5$ go test -bench=. |
不测不知道,一测吓一跳,其实前2个方法差不多很正常,但是第三个方法性能很好,其主要原因是因为Sprintf的性能比较差导致,不过**md5.New()**这种写法也比较慢。
5.最佳写法
最终得出结论,性能最高的md5写法是这种,推荐大家使用:
1 | func MD5(s string) string { |
6.Sha1
最后说个题外话,Go里面Sha1的写法和Md5几乎一致,只需要要把md5改成sha1即可:
1 | func Sha1(s string) string { |
我也测了一下性能,它们之间的差距很小,md5是163ns/op,sha1是206ns/op,毕竟sha1比md5长一点。。。