最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Golang對于用戶密碼的加密解決方案

 更新時間:2025年12月29日 10:39:32   作者:福狼owo  
本文介紹了多種密碼加密方案的性能比較,包括MD5、PBKDF2、Argon2、Scrypt和bcrypt,根據(jù)性能和安全性,Argon2被認為是高安全性要求系統(tǒng)中的最優(yōu)方案,但在高并發(fā)場景下需要控制資源消耗,Scrypt則在平衡安全性和成本方面表現(xiàn)出色,感興趣的朋友跟隨小編一起看看吧

對于用戶密碼的存儲一直以來都是用戶系統(tǒng)的重中之重,對安全性有較高要求的系統(tǒng)會采取雙端加密,即前端使用非對稱加密+Base64編碼,后端用私鑰解密后再采用不可逆的哈希算法進行加密。

筆者以前一直都是在用md5加鹽做玩具項目,直到最近才注意到這個問題,md5早就不再安全,雖然有動態(tài)加鹽之類的方案但或許還是有些太費力了,于是探索了一下其他的加密方案。

測試環(huán)境:

        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 / 內(nèi)存消耗極低卻完全不安全(已被破解)

僅用于非敏感數(shù)據(jù)的哈希,絕對禁止用于密碼存儲;

實現(xiàn)

// 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)
	}
}
測試名稱迭代次數(shù)單次操作耗時單次內(nèi)存分配單次分配次數(shù)
BenchmarkMD5-167181100165.1 ns/op80 B/op3 allocs/op

PBKDF2

CPU 密集型,中等安全,依賴迭代次數(shù)

實現(xiàn)

// ====================== PBKDF2 ======================
// CPU資源消耗高, 安全性中
// PBKDF2 核心配置
const (
	PBKDF2Iterations = 100000 // 迭代次數(shù)
	PBKDF2KeyLength  = 32     // 密鑰長度 32字節(jié)=256位
	SaltLength       = 16     // 獨立鹽長度 16字節(jié)=128位
)
// CreatePBKDF2 生成PBKDF2哈希
// return: 鹽(base64):迭代次數(shù):哈希值(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,               // 迭代次數(shù)
		PBKDF2KeyLength,                // 派生密鑰長度
		sha256.New,                     // 底層哈希函數(shù)(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("存儲的哈希格式錯誤")
	}
	// 解析各部分參數(shù)
	saltBase64 := parts[0]
	iterStr := parts[1]
	hashBase64 := parts[2]
	// 解析迭代次數(shù)
	iterations, err := strconv.Atoi(iterStr)
	if err != nil {
		return false, fmt.Errorf("解析迭代次數(shù)失敗: %w", err)
	}
	if iterations <= 0 {
		return false, errors.New("迭代次數(shù)必須大于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("預(yù)生成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)
		}
	}
}        
測試名稱迭代次數(shù)單次操作耗時單次內(nèi)存分配單次分配次數(shù)
BenchmarkPBKDF2_Create-168114668586 ns/op1114 B/op19 allocs/op
BenchmarkPBKDF2_Verify-168014748718 ns/op924 B/op14 allocs/op

Argon2

CPU + 內(nèi)存雙密集型,最高安全(2015 年密碼哈希競賽冠軍,專門抵御 GPU/ASIC 暴力破解)

適合金融、政務(wù)此類高安全的系統(tǒng),需要注意高并發(fā)下內(nèi)存資源的管控

實現(xiàn)

// ====================== Argon2 ======================
// CPU+內(nèi)存資源消耗高, 安全性高
// Argon2 配置
const (
	argon2Time    = 3         // 迭代次數(shù)
	argon2Memory  = 64 * 1024 // 內(nèi)存占用(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),
	)
	// 拼接參數(shù)
	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) {
	// 拆分參數(shù)
	parts := strings.Split(storedHash, ":")
	if len(parts) != 5 {
		return false, fmt.Errorf("哈希格式錯誤, 需5部分, 實際%d部分", len(parts))
	}
	// 拆分參數(shù)
	saltB64 := parts[0]
	timeStr := parts[1]
	memoryStr := parts[2]
	threadsStr := parts[3]
	hashB64 := parts[4]
	// 解析數(shù)值參數(shù)
	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次、內(nèi)存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("預(yù)生成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)
		}
	}
}
測試名稱迭代次數(shù)單次操作耗時單次內(nèi)存分配單次分配次數(shù)
BenchmarkArgon2_Create-163433258774 ns/op67121944 B/op104 allocs/op
BenchmarkArgon2_Verify-163433207082 ns/op67118572 B/op92 allocs/op

scrypt

內(nèi)存密集型,高安全,高并發(fā)場景下的權(quán)衡選擇

內(nèi)存占用比Argon2更小且安全性也足夠,在高并發(fā)場景下可能會用到

實現(xiàn)

// ====================== scrypt ======================
// 內(nèi)存資源消耗高, 安全性中
// scrypt 配置
const (
	scryptN      = 1 << 14 // 內(nèi)存因子(2^14=16384, 值越大內(nèi)存占用越高)
	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
}

性能測試

內(nèi)存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("預(yù)生成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)
		}
	}
}
測試名稱迭代次數(shù)單次操作耗時單次內(nèi)存分配單次分配次數(shù)
BenchmarkScrypt_Create-163730749478 ns/op16784634 B/op33 allocs/op
BenchmarkScrypt_Verify-163730630054 ns/op16781952 B/op26 allocs/op

bcrypt

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

實現(xiàn)

// ====================== bcrypt ======================
// 內(nèi)存資源消耗高, 安全性中, 速度慢
// 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("預(yù)生成bcrypt哈希失敗: %v", err)
	}
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_ = VerifyBcrypt(testPassword, hash)
	}
}
測試名稱迭代次數(shù)單次操作耗時單次內(nèi)存分配單次分配次數(shù)
BenchmarkBcrypt_Create-165208128060 ns/op5824 B/op13 allocs/op
BenchmarkBcrypt_Verify-165209431180 ns/op5339 B/op13 allocs/op

測試總結(jié)

5種加密方案各具特色,但 md5/bcrypt 雖然實現(xiàn)簡單但是具有明顯劣勢不應(yīng)該被選擇。

在高安全性要求的系統(tǒng)中選用 Argon2 是最優(yōu)方案,但需要對登錄/注冊接口的流量進行一定的限制避免資源耗盡。

而平衡安全性和成本的情況下 scrypt 是最優(yōu)方案。

PBKDF2 對內(nèi)存幾乎沒有需求,在小型系統(tǒng)種是最優(yōu)方案。

測試名稱迭代次數(shù)單次操作耗時單次內(nèi)存分配單次分配次數(shù)
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("解析迭代次數(shù)失敗: %w", err)
	}
	if iterations <= 0 {
		return false, errors.New("迭代次數(shù)必須大于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("預(yù)生成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("預(yù)生成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("預(yù)生成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("預(yù)生成bcrypt哈希失敗: %v", err)
	}
	b.StartTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_ = VerifyBcrypt(testPassword, hash)
	}
}

到此這篇關(guān)于探索Golang對于用戶密碼的加密方案的文章就介紹到這了,更多相關(guān)goland密碼加密內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go開源項目分布式唯一ID生成系統(tǒng)

    Go開源項目分布式唯一ID生成系統(tǒng)

    這篇文章主要為大家介紹了Go開源項目分布式唯一ID生成系統(tǒng)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • 從零封裝Gin框架配置初始化全局變量

    從零封裝Gin框架配置初始化全局變量

    這篇文章主要為大家介紹了從零封裝Gin框架配置初始化全局變量,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2024-01-01
  • Go語言實現(xiàn)自動填寫古詩詞實例代碼

    Go語言實現(xiàn)自動填寫古詩詞實例代碼

    這篇文章主要給大家介紹了關(guān)于Go語言實現(xiàn)自動填寫古詩詞的相關(guān)資料,這是最近在項目中遇到的一個需求,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-03-03
  • Golang運行報錯找不到包:package?xxx?is?not?in?GOROOT的解決過程

    Golang運行報錯找不到包:package?xxx?is?not?in?GOROOT的解決過程

    這篇文章主要給大家介紹了關(guān)于Golang運行報錯找不到包:package?xxx?is?not?in?GOROOT的解決過程,文中通過圖文介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2022-07-07
  • Go映射的使用

    Go映射的使用

    Go提供了另一個重要的數(shù)據(jù)類型,稱為map,它將唯一鍵映射到值,本文主要介紹了Go映射的使用,包括聲明映射、初始化映射、操作映射等,感興趣的可以了解一下
    2023-11-11
  • Go?處理大數(shù)組使用?for?range?和?for?循環(huán)的區(qū)別

    Go?處理大數(shù)組使用?for?range?和?for?循環(huán)的區(qū)別

    這篇文章主要介紹了Go處理大數(shù)組使用for?range和for循環(huán)的區(qū)別,對于遍歷大數(shù)組而言,for循環(huán)能比for?range循環(huán)更高效與穩(wěn)定,這一點在數(shù)組元素為結(jié)構(gòu)體類型更加明顯,下文具體分析感興趣得小伙伴可以參考一下
    2022-05-05
  • 一文帶你掌握Go語言中文件的寫入操作

    一文帶你掌握Go語言中文件的寫入操作

    這篇文章主要和大家分享一下Go語言中文件的寫入操作,文中的示例代碼講解詳細,對我們學(xué)習(xí)Go語言有一定的幫助,需要的小伙伴可以參考一下
    2022-12-12
  • golang特有程序結(jié)構(gòu)入門教程

    golang特有程序結(jié)構(gòu)入門教程

    GO語言是一門不錯的編程語言能夠到達靜態(tài)編譯語言的安全和性能,在本文中重點給大家介紹goland特有程序結(jié)構(gòu)及引用類型別名的特征,感興趣的朋友跟隨小編一起看看吧
    2021-06-06
  • golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-08-08
  • Golang實現(xiàn)自己的Redis數(shù)據(jù)庫內(nèi)存實例探究

    Golang實現(xiàn)自己的Redis數(shù)據(jù)庫內(nèi)存實例探究

    這篇文章主要為大家介紹了Golang實現(xiàn)自己的Redis數(shù)據(jù)庫內(nèi)存實例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2024-01-01

最新評論

湖口县| 新兴县| 天峨县| 日照市| 册亨县| 乐东| 稷山县| 承德市| 宽甸| 麦盖提县| 阳山县| 唐河县| 西乌珠穆沁旗| 林甸县| 霞浦县| 固始县| 昌乐县| 故城县| 南部县| 喀喇沁旗| 周口市| 新乡市| 进贤县| 通化市| 什邡市| 邳州市| 高台县| 易门县| 垣曲县| 武宁县| 比如县| 安国市| 吉安县| 卓资县| 威海市| 波密县| 邯郸市| 临泉县| 皋兰县| 新密市| 绥中县|