Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > goland密码加密

Golang对于用户密码的加密解决方案

作者:福狼owo

本文介绍了多种密码加密方案的性能比较,包括MD5、PBKDF2、Argon2、Scrypt和bcrypt,根据性能和安全性,Argon2被认为是高安全性要求系统中的最优方案,但在高并发场景下需要控制资源消耗,Scrypt则在平衡安全性和成本方面表现出色,感兴趣的朋友跟随小编一起看看吧

对于用户密码的存储一直以来都是用户系统的重中之重,对安全性有较高要求的系统会采取双端加密,即前端使用非对称加密+Base64编码,后端用私钥解密后再采用不可逆的哈希算法进行加密。

笔者以前一直都是在用md5加盐做玩具项目,直到最近才注意到这个问题,md5早就不再安全,虽然有动态加盐之类的方案但或许还是有些太费力了,于是探索了一下其他的加密方案。

测试环境:

        CPU: AMD Ryzen 7 5800H with Radeon Graphics

        go test -bench=. -benchmem

使用库:

import (
	"crypto/md5"
	"crypto/rand"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"golang.org/x/crypto/argon2"
	"golang.org/x/crypto/bcrypt"
	"golang.org/x/crypto/pbkdf2"
	"golang.org/x/crypto/scrypt"
)

MD5

极致高效,CPU / 内存消耗极低却完全不安全(已被破解)

仅用于非敏感数据的哈希,绝对禁止用于密码存储

实现

// CreateMD5 MD5 加密
func CreateMD5(str string) string {
	h := md5.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum(nil))
}

性能测试

func BenchmarkMD5(b *testing.B) {
	pwd := testPassword + testAuthSalt
	b.ResetTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_ = CreateMD5(pwd)
	}
}
测试名称迭代次数单次操作耗时单次内存分配单次分配次数
BenchmarkMD5-167181100165.1 ns/op80 B/op3 allocs/op

PBKDF2

CPU 密集型,中等安全,依赖迭代次数

实现

// ====================== PBKDF2 ======================
// CPU资源消耗高, 安全性中
// PBKDF2 核心配置
const (
	PBKDF2Iterations = 100000 // 迭代次数
	PBKDF2KeyLength  = 32     // 密钥长度 32字节=256位
	SaltLength       = 16     // 独立盐长度 16字节=128位
)
// CreatePBKDF2 生成PBKDF2哈希
// return: 盐(base64):迭代次数:哈希值(base64)
func CreatePBKDF2(password string) (string, error) {
	// 生成每个用户的独立随机盐
	salt := make([]byte, SaltLength)
	n, err := rand.Read(salt)
	if err != nil {
		return "", fmt.Errorf("生成随机盐失败: %w", err)
	}
	if n != SaltLength {
		return "", errors.New("生成的盐长度不足")
	}
	// 拼接全局盐
	globalSalt := AuthSalt
	passwordWithGlobalSalt := password + globalSalt
	// 计算PBKDF2哈希
	hash := pbkdf2.Key(
		[]byte(passwordWithGlobalSalt), // 密码+全局盐
		salt,                           // 独立随机盐
		PBKDF2Iterations,               // 迭代次数
		PBKDF2KeyLength,                // 派生密钥长度
		sha256.New,                     // 底层哈希函数(HMAC-SHA256)
	)
	// 拼接存储字符串
	saltBase64 := base64.StdEncoding.EncodeToString(salt)
	hashBase64 := base64.StdEncoding.EncodeToString(hash)
	storedStr := fmt.Sprintf("%s:%d:%s", saltBase64, PBKDF2Iterations, hashBase64)
	return storedStr, nil
}
// VerifyPBKDF2 验证密码是否匹配PBKDF2哈希
// password: 明文密码
// storedStr: 加密后的哈希字符串
func VerifyPBKDF2(password string, storedStr string) (bool, error) {
	// 拆分存储的字符串
	parts := strings.Split(storedStr, ":")
	if len(parts) != 3 {
		return false, errors.New("存储的哈希格式错误")
	}
	// 解析各部分参数
	saltBase64 := parts[0]
	iterStr := parts[1]
	hashBase64 := parts[2]
	// 解析迭代次数
	iterations, err := strconv.Atoi(iterStr)
	if err != nil {
		return false, fmt.Errorf("解析迭代次数失败: %w", err)
	}
	if iterations <= 0 {
		return false, errors.New("迭代次数必须大于0")
	}
	// 解码盐
	salt, err := base64.StdEncoding.DecodeString(saltBase64)
	if err != nil {
		return false, fmt.Errorf("解码盐失败: %w", err)
	}
	// 解码原始哈希值
	originalHash, err := base64.StdEncoding.DecodeString(hashBase64)
	if err != nil {
		return false, fmt.Errorf("解码哈希值失败: %w", err)
	}
	// 拼接全局盐
	globalSalt := AuthSalt
	passwordWithGlobalSalt := password + globalSalt
	// 重新计算哈希
	newHash := pbkdf2.Key(
		[]byte(passwordWithGlobalSalt),
		salt,
		iterations,
		len(originalHash),
		sha256.New,
	)
	// 安全比较哈希, 等价于 hmac.Equal(newHash, originalHash)
	if subtle.ConstantTimeCompare(newHash, originalHash) == 1 {
		return true, nil
	}
	return false, nil
}

性能测试

100000次迭代、密钥长度32、盐长度16

func BenchmarkPBKDF2_Create(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_, err := CreatePBKDF2(testPassword)
		if err != nil {
			b.Fatalf("PBKDF2加密失败: %v", err)
		}
	}
}
func BenchmarkPBKDF2_Verify(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	hash, err := CreatePBKDF2(testPassword)
	if err != nil {
		b.Fatalf("预生成PBKDF2哈希失败: %v", err)
	}
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_, err := VerifyPBKDF2(testPassword, hash)
		if err != nil {
			b.Fatalf("PBKDF2验证失败: %v", err)
		}
	}
}        
测试名称迭代次数单次操作耗时单次内存分配单次分配次数
BenchmarkPBKDF2_Create-168114668586 ns/op1114 B/op19 allocs/op
BenchmarkPBKDF2_Verify-168014748718 ns/op924 B/op14 allocs/op

Argon2

CPU + 内存双密集型,最高安全(2015 年密码哈希竞赛冠军,专门抵御 GPU/ASIC 暴力破解)

适合金融、政务此类高安全的系统,需要注意高并发下内存资源的管控

实现

// ====================== Argon2 ======================
// CPU+内存资源消耗高, 安全性高
// Argon2 配置
const (
	argon2Time    = 3         // 迭代次数
	argon2Memory  = 64 * 1024 // 内存占用(64MB)
	argon2Threads = 4         // 并行度
	argon2KeyLen  = 32        // 派生密钥长度
	saltLen       = 16        // 盐长度
)
// CreateArgon2 生成 Argon2id 哈希
func CreateArgon2(password string) (string, error) {
	// 生成随机盐
	salt := make([]byte, saltLen)
	if _, err := rand.Read(salt); err != nil {
		return "", fmt.Errorf("生成盐失败: %w", err)
	}
	// 计算 Argon2id 哈希
	globalSalt := AuthSalt
	pwd := []byte(password + globalSalt)
	hash := argon2.IDKey(
		pwd,
		salt,
		uint32(argon2Time),
		uint32(argon2Memory),
		uint8(argon2Threads),
		uint32(argon2KeyLen),
	)
	// 拼接参数
	saltB64 := base64.StdEncoding.EncodeToString(salt)
	hashB64 := base64.StdEncoding.EncodeToString(hash)
	storedStr := fmt.Sprintf("%s:%d:%d:%d:%s",
		saltB64, argon2Time, argon2Memory, argon2Threads, hashB64)
	return storedStr, nil
}
// VerifyArgon2 验证 Argon2 哈希
func VerifyArgon2(password, storedHash string) (bool, error) {
	// 拆分参数
	parts := strings.Split(storedHash, ":")
	if len(parts) != 5 {
		return false, fmt.Errorf("哈希格式错误, 需5部分, 实际%d部分", len(parts))
	}
	// 拆分参数
	saltB64 := parts[0]
	timeStr := parts[1]
	memoryStr := parts[2]
	threadsStr := parts[3]
	hashB64 := parts[4]
	// 解析数值参数
	time, err := strconv.Atoi(timeStr)
	if err != nil {
		return false, fmt.Errorf("解析time失败: %w", err)
	}
	memory, err := strconv.Atoi(memoryStr)
	if err != nil {
		return false, fmt.Errorf("解析memory失败: %w", err)
	}
	threads, err := strconv.Atoi(threadsStr)
	if err != nil {
		return false, fmt.Errorf("解析threads失败: %w", err)
	}
	// 解码盐和原始哈希
	salt, err := base64.StdEncoding.DecodeString(saltB64)
	if err != nil {
		return false, fmt.Errorf("解码盐失败: %w", err)
	}
	originalHash, err := base64.StdEncoding.DecodeString(hashB64)
	if err != nil {
		return false, fmt.Errorf("解码哈希失败: %w", err)
	}
	// 重新计算哈希
	globalSalt := AuthSalt
	pwd := []byte(password + globalSalt)
	newHash := argon2.IDKey(
		pwd,
		salt,
		uint32(time),
		uint32(memory),
		uint8(threads),
		uint32(len(originalHash)),
	)
	// 安全比较
	if subtle.ConstantTimeCompare(newHash, originalHash) == 1 {
		return true, nil
	}
	return false, nil
}

性能测试

迭代3次、内存64MB、并行度4、派生密钥长度32、盐长度16

func BenchmarkArgon2_Create(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_, err := CreateArgon2(testPassword)
		if err != nil {
			b.Fatalf("Argon2加密失败: %v", err)
		}
	}
}
func BenchmarkArgon2_Verify(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	hash, err := CreateArgon2(testPassword)
	if err != nil {
		b.Fatalf("预生成Argon2哈希失败: %v", err)
	}
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_, err := VerifyArgon2(testPassword, hash)
		if err != nil {
			b.Fatalf("Argon2验证失败: %v", err)
		}
	}
}
测试名称迭代次数单次操作耗时单次内存分配单次分配次数
BenchmarkArgon2_Create-163433258774 ns/op67121944 B/op104 allocs/op
BenchmarkArgon2_Verify-163433207082 ns/op67118572 B/op92 allocs/op

scrypt

内存密集型,高安全,高并发场景下的权衡选择

内存占用比Argon2更小且安全性也足够,在高并发场景下可能会用到

实现

// ====================== scrypt ======================
// 内存资源消耗高, 安全性中
// scrypt 配置
const (
	scryptN      = 1 << 14 // 内存因子(2^14=16384, 值越大内存占用越高)
	scryptR      = 8       // 块大小
	scryptP      = 1       // 并行因子
	scryptKeyLen = 32      // 密钥长度
)
// CreateScrypt 生成 scrypt 哈希
func CreateScrypt(password string) (string, error) {
	// 生成随机盐
	salt := make([]byte, 16)
	if _, err := rand.Read(salt); err != nil {
		return "", err
	}
	// 计算 scrypt 哈希
	globalSalt := AuthSalt
	pwd := []byte(password + globalSalt)
	hash, err := scrypt.Key(pwd, salt, scryptN, scryptR, scryptP, scryptKeyLen)
	if err != nil {
		return "", err
	}
	// 拼接盐和哈希
	saltB64 := base64.StdEncoding.EncodeToString(salt)
	hashB64 := base64.StdEncoding.EncodeToString(hash)
	return fmt.Sprintf("%s:%s", saltB64, hashB64), nil
}
// VerifyScrypt 验证 scrypt 哈希
func VerifyScrypt(password, storedHash string) (bool, error) {
	parts := strings.Split(storedHash, ":")
	if len(parts) != 2 {
		return false, errors.New("哈希格式错误")
	}
	salt, _ := base64.StdEncoding.DecodeString(parts[0])
	originalHash, _ := base64.StdEncoding.DecodeString(parts[1])
	globalSalt := AuthSalt
	pwd := []byte(password + globalSalt)
	newHash, err := scrypt.Key(pwd, salt, scryptN, scryptR, scryptP, len(originalHash))
	if err != nil {
		return false, err
	}
	return subtle.ConstantTimeCompare(newHash, originalHash) == 1, nil
}

性能测试

内存16MB、块大小8、并行度1、密钥长度32

func BenchmarkScrypt_Create(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_, err := CreateScrypt(testPassword)
		if err != nil {
			b.Fatalf("scrypt加密失败: %v", err)
		}
	}
}
func BenchmarkScrypt_Verify(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	hash, err := CreateScrypt(testPassword)
	if err != nil {
		b.Fatalf("预生成scrypt哈希失败: %v", err)
	}
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_, err := VerifyScrypt(testPassword, hash)
		if err != nil {
			b.Fatalf("scrypt验证失败: %v", err)
		}
	}
}
测试名称迭代次数单次操作耗时单次内存分配单次分配次数
BenchmarkScrypt_Create-163730749478 ns/op16784634 B/op33 allocs/op
BenchmarkScrypt_Verify-163730630054 ns/op16781952 B/op26 allocs/op

bcrypt

CPU 密集型,中等安全,速度非常慢,好像是1999年的老东西了吧

实现

// ====================== bcrypt ======================
// 内存资源消耗高, 安全性中, 速度慢
// CreateBcrypt 生成 bcrypt 哈希
func CreateBcrypt(password string) (string, error) {
	// 密码+全局盐
	globalSalt := AuthSalt
	pwd := []byte(password + globalSalt)
	// 成本因子
	cost := 12
	hash, err := bcrypt.GenerateFromPassword(pwd, cost)
	if err != nil {
		return "", err
	}
	return string(hash), nil
}
// VerifyBcrypt 验证 bcrypt 哈希
func VerifyBcrypt(password, storedHash string) bool {
	globalSalt := AuthSalt
	pwd := []byte(password + globalSalt)
	// bcrypt.CompareHashAndPassword 自动处理盐和成本因子
	err := bcrypt.CompareHashAndPassword([]byte(storedHash), pwd)
	return err == nil
}

性能测试

func BenchmarkBcrypt_Create(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_, err := CreateBcrypt(testPassword)
		if err != nil {
			b.Fatalf("bcrypt加密失败: %v", err)
		}
	}
}
func BenchmarkBcrypt_Verify(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	hash, err := CreateBcrypt(testPassword)
	if err != nil {
		b.Fatalf("预生成bcrypt哈希失败: %v", err)
	}
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_ = VerifyBcrypt(testPassword, hash)
	}
}
测试名称迭代次数单次操作耗时单次内存分配单次分配次数
BenchmarkBcrypt_Create-165208128060 ns/op5824 B/op13 allocs/op
BenchmarkBcrypt_Verify-165209431180 ns/op5339 B/op13 allocs/op

测试总结

5种加密方案各具特色,但 md5/bcrypt 虽然实现简单但是具有明显劣势不应该被选择。

在高安全性要求的系统中选用 Argon2 是最优方案,但需要对登录/注册接口的流量进行一定的限制避免资源耗尽。

而平衡安全性和成本的情况下 scrypt 是最优方案。

PBKDF2 对内存几乎没有需求,在小型系统种是最优方案。

测试名称迭代次数单次操作耗时单次内存分配单次分配次数
BenchmarkMD5-167181100165.1 ns/op80 B/op3 allocs/op
BenchmarkPBKDF2_Create-168114668586 ns/op1114 B/op19 allocs/op
BenchmarkPBKDF2_Verify-168014748718 ns/op924 B/op14 allocs/op
BenchmarkArgon2_Create-163433258774 ns/op67121944 B/op104 allocs/op
BenchmarkArgon2_Verify-163433207082 ns/op67118572 B/op92 allocs/op
BenchmarkScrypt_Create-163730749478 ns/op16784634 B/op33 allocs/op
BenchmarkScrypt_Verify-163730630054 ns/op16781952 B/op26 allocs/op
BenchmarkBcrypt_Create-165208128060 ns/op5824 B/op13 allocs/op
BenchmarkBcrypt_Verify-165209431180 ns/op5339 B/op13 allocs/op

完整代码

package crypto
import (
	"crypto/md5"
	"crypto/rand"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"golang.org/x/crypto/argon2"
	"golang.org/x/crypto/bcrypt"
	"golang.org/x/crypto/pbkdf2"
	"golang.org/x/crypto/scrypt"
)
var AuthSalt = "GolangIsGood"
// ====================== MD5 ======================
func CreateMD5(str string) string {
	h := md5.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum(nil))
}
// ====================== PBKDF2 ======================
const (
	PBKDF2Iterations = 100000
	PBKDF2KeyLength  = 32
	SaltLength       = 16
)
func CreatePBKDF2(password string) (string, error) {
	salt := make([]byte, SaltLength)
	n, err := rand.Read(salt)
	if err != nil {
		return "", fmt.Errorf("生成随机盐失败: %w", err)
	}
	if n != SaltLength {
		return "", errors.New("生成的盐长度不足")
	}
	globalSalt := AuthSalt
	passwordWithGlobalSalt := password + globalSalt
	hash := pbkdf2.Key(
		[]byte(passwordWithGlobalSalt),
		salt,
		PBKDF2Iterations,
		PBKDF2KeyLength,
		sha256.New,
	)
	saltBase64 := base64.StdEncoding.EncodeToString(salt)
	hashBase64 := base64.StdEncoding.EncodeToString(hash)
	storedStr := fmt.Sprintf("%s:%d:%s", saltBase64, PBKDF2Iterations, hashBase64)
	return storedStr, nil
}
func VerifyPBKDF2(password string, storedStr string) (bool, error) {
	parts := strings.Split(storedStr, ":")
	if len(parts) != 3 {
		return false, errors.New("存储的哈希格式错误")
	}
	saltBase64 := parts[0]
	iterStr := parts[1]
	hashBase64 := parts[2]
	iterations, err := strconv.Atoi(iterStr)
	if err != nil {
		return false, fmt.Errorf("解析迭代次数失败: %w", err)
	}
	if iterations <= 0 {
		return false, errors.New("迭代次数必须大于0")
	}
	salt, err := base64.StdEncoding.DecodeString(saltBase64)
	if err != nil {
		return false, fmt.Errorf("解码盐失败: %w", err)
	}
	originalHash, err := base64.StdEncoding.DecodeString(hashBase64)
	if err != nil {
		return false, fmt.Errorf("解码哈希值失败: %w", err)
	}
	globalSalt := AuthSalt
	passwordWithGlobalSalt := password + globalSalt
	newHash := pbkdf2.Key(
		[]byte(passwordWithGlobalSalt),
		salt,
		iterations,
		len(originalHash),
		sha256.New,
	)
	if subtle.ConstantTimeCompare(newHash, originalHash) == 1 {
		return true, nil
	}
	return false, nil
}
// ====================== Argon2 ======================
const (
	argon2Time    = 3
	argon2Memory  = 64 * 1024
	argon2Threads = 4
	argon2KeyLen  = 32
	saltLen       = 16
)
func CreateArgon2(password string) (string, error) {
	salt := make([]byte, saltLen)
	if _, err := rand.Read(salt); err != nil {
		return "", fmt.Errorf("生成盐失败: %w", err)
	}
	globalSalt := AuthSalt
	pwd := []byte(password + globalSalt)
	hash := argon2.IDKey(
		pwd,
		salt,
		uint32(argon2Time),
		uint32(argon2Memory),
		uint8(argon2Threads),
		uint32(argon2KeyLen),
	)
	saltB64 := base64.StdEncoding.EncodeToString(salt)
	hashB64 := base64.StdEncoding.EncodeToString(hash)
	storedStr := fmt.Sprintf("%s:%d:%d:%d:%s", saltB64, argon2Time, argon2Memory, argon2Threads, hashB64)
	return storedStr, nil
}
func VerifyArgon2(password, storedHash string) (bool, error) {
	parts := strings.Split(storedHash, ":")
	if len(parts) != 5 {
		return false, fmt.Errorf("哈希格式错误, 需5部分, 实际%d部分", len(parts))
	}
	saltB64 := parts[0]
	timeStr := parts[1]
	memoryStr := parts[2]
	threadsStr := parts[3]
	hashB64 := parts[4]
	time, err := strconv.Atoi(timeStr)
	if err != nil {
		return false, fmt.Errorf("解析time失败: %w", err)
	}
	memory, err := strconv.Atoi(memoryStr)
	if err != nil {
		return false, fmt.Errorf("解析memory失败: %w", err)
	}
	threads, err := strconv.Atoi(threadsStr)
	if err != nil {
		return false, fmt.Errorf("解析threads失败: %w", err)
	}
	salt, err := base64.StdEncoding.DecodeString(saltB64)
	if err != nil {
		return false, fmt.Errorf("解码盐失败: %w", err)
	}
	originalHash, err := base64.StdEncoding.DecodeString(hashB64)
	if err != nil {
		return false, fmt.Errorf("解码哈希失败: %w", err)
	}
	globalSalt := AuthSalt
	pwd := []byte(password + globalSalt)
	newHash := argon2.IDKey(
		pwd,
		salt,
		uint32(time),
		uint32(memory),
		uint8(threads),
		uint32(len(originalHash)),
	)
	return subtle.ConstantTimeCompare(newHash, originalHash) == 1, nil
}
// ====================== scrypt ======================
const (
	scryptN      = 1 << 14
	scryptR      = 8
	scryptP      = 1
	scryptKeyLen = 32
)
func CreateScrypt(password string) (string, error) {
	salt := make([]byte, 16)
	if _, err := rand.Read(salt); err != nil {
		return "", err
	}
	globalSalt := AuthSalt
	pwd := []byte(password + globalSalt)
	hash, err := scrypt.Key(pwd, salt, scryptN, scryptR, scryptP, scryptKeyLen)
	if err != nil {
		return "", err
	}
	saltB64 := base64.StdEncoding.EncodeToString(salt)
	hashB64 := base64.StdEncoding.EncodeToString(hash)
	return fmt.Sprintf("%s:%s", saltB64, hashB64), nil
}
func VerifyScrypt(password, storedHash string) (bool, error) {
	parts := strings.Split(storedHash, ":")
	if len(parts) != 2 {
		return false, errors.New("哈希格式错误")
	}
	salt, _ := base64.StdEncoding.DecodeString(parts[0])
	originalHash, _ := base64.StdEncoding.DecodeString(parts[1])
	globalSalt := AuthSalt
	pwd := []byte(password + globalSalt)
	newHash, err := scrypt.Key(pwd, salt, scryptN, scryptR, scryptP, len(originalHash))
	if err != nil {
		return false, err
	}
	return subtle.ConstantTimeCompare(newHash, originalHash) == 1, nil
}
// ====================== bcrypt ======================
func CreateBcrypt(password string) (string, error) {
	globalSalt := AuthSalt
	pwd := []byte(password + globalSalt)
	cost := 12
	hash, err := bcrypt.GenerateFromPassword(pwd, cost)
	if err != nil {
		return "", err
	}
	return string(hash), nil
}
func VerifyBcrypt(password, storedHash string) bool {
	globalSalt := AuthSalt
	pwd := []byte(password + globalSalt)
	err := bcrypt.CompareHashAndPassword([]byte(storedHash), pwd)
	return err == nil
}
package crypto
import (
	"testing"
)
const (
	testPassword = "123456@gofurry"
	testAuthSalt = "GolangIsGood"
)
// ====================== Benchmark ======================
func BenchmarkMD5(b *testing.B) {
	pwd := testPassword + testAuthSalt
	b.ResetTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_ = CreateMD5(pwd)
	}
}
func BenchmarkPBKDF2_Create(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_, err := CreatePBKDF2(testPassword)
		if err != nil {
			b.Fatalf("PBKDF2加密失败: %v", err)
		}
	}
}
func BenchmarkPBKDF2_Verify(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	hash, err := CreatePBKDF2(testPassword)
	if err != nil {
		b.Fatalf("预生成PBKDF2哈希失败: %v", err)
	}
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_, err := VerifyPBKDF2(testPassword, hash)
		if err != nil {
			b.Fatalf("PBKDF2验证失败: %v", err)
		}
	}
}
func BenchmarkArgon2_Create(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_, err := CreateArgon2(testPassword)
		if err != nil {
			b.Fatalf("Argon2加密失败: %v", err)
		}
	}
}
func BenchmarkArgon2_Verify(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	hash, err := CreateArgon2(testPassword)
	if err != nil {
		b.Fatalf("预生成Argon2哈希失败: %v", err)
	}
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_, err := VerifyArgon2(testPassword, hash)
		if err != nil {
			b.Fatalf("Argon2验证失败: %v", err)
		}
	}
}
func BenchmarkScrypt_Create(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_, err := CreateScrypt(testPassword)
		if err != nil {
			b.Fatalf("scrypt加密失败: %v", err)
		}
	}
}
func BenchmarkScrypt_Verify(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	hash, err := CreateScrypt(testPassword)
	if err != nil {
		b.Fatalf("预生成scrypt哈希失败: %v", err)
	}
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_, err := VerifyScrypt(testPassword, hash)
		if err != nil {
			b.Fatalf("scrypt验证失败: %v", err)
		}
	}
}
func BenchmarkBcrypt_Create(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_, err := CreateBcrypt(testPassword)
		if err != nil {
			b.Fatalf("bcrypt加密失败: %v", err)
		}
	}
}
func BenchmarkBcrypt_Verify(b *testing.B) {
	b.StopTimer()
	AuthSalt = testAuthSalt
	hash, err := CreateBcrypt(testPassword)
	if err != nil {
		b.Fatalf("预生成bcrypt哈希失败: %v", err)
	}
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_ = VerifyBcrypt(testPassword, hash)
	}
}

到此这篇关于探索Golang对于用户密码的加密方案的文章就介绍到这了,更多相关goland密码加密内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文