Golang封装加盐和加密次数的MD5函数
最近在使用Gin重构之前用SpringBoot写的后台程序,数据库中保存的密码是加了盐并经过两次hash的MD5值,之前SpringBoot是调用的Shiro进行两次MD5运算。查了一圈发现Golang的MD5工具库并没有提供加盐、加密次数的封装,并且也没有博客提及,所以这里记录一下自己封装的带加盐和加密次数的MD5函数
Golang中使用MD5库
Golang自带的crypto加密库中有MD5函数的实现,主要使用方式有两种,一种是调用write将要计算的byte数组提前写入block块中,调用sum时传入nil;二是直接将byte数组传入sum函数,如下所示:
package utils import ( "crypto/md5" "encoding/hex" ) // 方式一通过Write传参 func MD5(str string) string { b := []byte(str) h := md5.New() h.Write(b) return hex.EncodeToString(h.Sum(nil)) } // 方式二通过Sum传参 func MD5_2(str string) string { b := []byte(str) h := md5.New() return hex.EncodeToString(h.Sum(b)) } 复制代码
若要加盐的话,在第一种方式的基础上再把salt給write进去,如下所示:
func MD5_SALT(str string, salt string) string { b := []byte(str) s := []byte(salt) h := md5.New() h.Write(s) // 先写盐值 h.Write(b) return hex.EncodeToString(h.Sum(nil)) } 复制代码
错误的多次加密封装
查到MD5函数的加盐使用后,就想当然的以为多次加密应该就是循环加盐的过程,调用下面的封装后发现算出来的盐值不对,网上也没找到多次加密的文章,所以就去找了SpringBoot中的封装。
// 错误的加盐并多次加密 func MD5_SALT_MULT(str string, salt string, times int) string { b := []byte(str) s := []byte(salt) h := md5.New() var res []byte for i := 0; i < times; i++ { h.Write(s) h.Write(b) res = h.Sum(nil) b = res } return hex.EncodeToString(res) } 复制代码
Java加盐并多次加密的MD5函数
找了找以前的SpringBoot项目,发现当时调用了Shiro框架的MD5封装,只需要将盐值、加密次数传入SimpleHash函数就行了,当时并没思考后面是怎么运算的。
import org.apache.shiro.crypto.RandomNumberGenerator; import org.apache.shiro.crypto.SecureRandomNumberGenerator; import org.apache.shiro.crypto.hash.SimpleHash; import org.apache.shiro.util.ByteSource; public class PasswordHelper { //随机字符串生成器,用于生成盐值 private RandomNumberGenerator numberGenerator = new SecureRandomNumberGenerator(); //哈希散列算法——md5 public static final String ALGORITHM_NAME = "md5"; //散列次数2 public static final int HASH_ITERATION = 2; /** * 加密用户 * @param user 用户,用户名(name)、密码(pwd)、盐\加密因子(salt) */ public String encryptPassword(String password){ if (password == null || "".equals(password)) return; //生成加密因子,保存盐。 String salt = numberGenerator.nextBytes().toHex(); //加密密码 SimpleHash(算法名,密码,盐的byte,次数).toHex() String newPassword = new SimpleHash(ALGORITHM_NAME, password, salt, HASH_ITERATION).toHex(); //更新密码 return newPassword; } } 复制代码
通过 SimpleHash函数向下找具体的实现,SimpleHash的核心代码如下,这才发现原来salt只用了一次,并且每次运算后还要调用rest函数清空digest中的block块,知道了具体过程后就是把这段Java代码翻译成Golang代码即可
package org.apache.shiro.crypto.hash; public class SimpleHash extends AbstractHash { protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) throws UnknownAlgorithmException { MessageDigest digest = this.getDigest(this.getAlgorithmName()); if (salt != null) { digest.reset(); digest.update(salt); } byte[] hashed = digest.digest(bytes); int iterations = hashIterations - 1; for(int i = 0; i < iterations; ++i) { digest.reset(); hashed = digest.digest(hashed); } return hashed; } } 复制代码
用Golang实现加盐并多次加密的MD5函数
Java中的MessageDigest对象和Golang的hash结构体可以看作是等价的,都有rest函数,只不过Java通过调用digest函数运算hash值而Golang通过调用Sum函数运算,只需要将上面的Java代码进行简单的翻译就行了。最终封装如下:
package utils import ( "crypto/md5" "encoding/hex" ) func MD5V(str string, salt string, iteration int) string { b := []byte(str) s := []byte(salt) h := md5.New() h.Write(s) // 先传入盐值,之前因为顺序错了卡了很久 h.Write(b) var res []byte res = h.Sum(nil) for i := 0; i < iteration-1; i++ { h.Reset() h.Write(res) res = h.Sum(nil) } return hex.EncodeToString(res) }
作者:Monkey_D_Newdun
链接:https://juejin.cn/post/7056324966005866504