Golang實(shí)現(xiàn)密碼加密的示例詳解
數(shù)據(jù)庫(kù)在存儲(chǔ)密碼時(shí),不能明文存儲(chǔ),需要加密后存儲(chǔ)
加密算法有很多種,比如:對(duì)稱(chēng)加密,非對(duì)稱(chēng)加密,哈希算法,密碼派生等
在加密之前,我們需要生成一些隨機(jī)數(shù),這些隨機(jī)數(shù)稱(chēng)為鹽
什么是鹽?為什么需要鹽?
鹽是一種加密算法中的一種參數(shù),它是一個(gè)隨機(jī)數(shù),用于增加破解密碼的難度
把密碼想象成一盤(pán)菜,鹽就是調(diào)料,不加調(diào)料就是原汁原味的菜,調(diào)料加多加少,菜口味就不一樣了
也就是說(shuō),鹽的多少,會(huì)影響到加密后的結(jié)果,如果不使用鹽,那么相同的密碼,加密后的結(jié)果是一樣的,這樣就很容易被破解
鹽的生成方式有很多種,比如:隨機(jī)數(shù),時(shí)間戳等
這里用隨機(jī)數(shù)來(lái)舉例:
- 定義一個(gè)字符串,里面包含了所有可能的字符
- 根據(jù)傳入的長(zhǎng)度,生成一個(gè)切片,并用隨機(jī)數(shù)填充
- 遍歷這個(gè)切片,取出隨機(jī)數(shù),然后對(duì)字符串長(zhǎng)度取余,得到一個(gè)索引,然后把這個(gè)索引對(duì)應(yīng)的字符,放到切片中
- 返回這個(gè)切片(這個(gè)切片就是鹽)
func generateSalt(length int) []byte {
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
salt := make([]byte, length)
rand.Read(salt)
for key, val := range salt {
salt[key] = alphanum[val%byte(len(alphanum))]
}
return salt
}生成鹽之后,我們就可以對(duì)密碼進(jìn)行加密了
加密后的密碼要使用 hex.EncodeToString 轉(zhuǎn)換成字符串
salt := generateSalt(16)
encoded := pbkdf2.Key([]byte("uccs"), salt, 100, 32, sha256.New)
encodedPwd := hex.EncodeToString(encodedPwd)因?yàn)檫@個(gè)加密算法是不可逆的,也就說(shuō)你不能通過(guò)加密后的密碼,反推出原始密碼
那用戶(hù)輸入密碼時(shí),如何知道密碼是否正確呢?
所以在存儲(chǔ)加密后的密碼時(shí),還要存儲(chǔ)鹽和加密算法的參數(shù)
這里使用的是 pbkdf2 算法,它的參數(shù)有:原始密碼,鹽,迭代次數(shù),密鑰長(zhǎng)度,哈希算法
所以最終存在數(shù)據(jù)的密碼是:
pwd := fmt.Sprintf("%s$%s$%d$%d$%s", encodedPwd, salt, 100, 32, "pbkdf2-sha256")
這樣在驗(yàn)證密碼時(shí),就可以通過(guò)這些參數(shù),把用戶(hù)輸入的密碼,加密后,再和數(shù)據(jù)庫(kù)中存儲(chǔ)的密碼進(jìn)行比較,如果相同,就說(shuō)明密碼正確
知識(shí)補(bǔ)充
當(dāng)然除了上面的加密方式,Golang中還有很多加密算法的實(shí)現(xiàn),下面小編就來(lái)為大家簡(jiǎn)單整理一下吧,希望對(duì)大家有所幫助
MD5加密
package main
import (
?? ?"crypto/md5"
?? ?"encoding/hex"
?? ?"fmt"
?? ?"io"
)
func main() {
?? ?h := md5.New()
?? ?io.WriteString(h, "123456")
?? ?sum := h.Sum(nil)
?? ?fmt.Println(hex.EncodeToString(sum[:]))
?? ?// e10adc3949ba59abbe56e057f20f883e
}凱撒密碼加密
package main
import (
"fmt"
"strings"
)
func caesar(r rune, shift int) rune {
// Shift character by specified number of places.
// ... If beyond range, shift backward or forward.
s := int(r) + shift
if s > 'z' {
return rune(s - 26)
} else if s < 'a' {
return rune(s + 26)
}
return rune(s)
}
func main() {
value := "test"
fmt.Println(value)
// Test the caesar method in a func argument to strings.Map.
value2 := strings.Map(func(r rune) rune {
return caesar(r, 18)
}, value)
value3 := strings.Map(func(r rune) rune {
return caesar(r, -18)
}, value2)
fmt.Println(value2, value3)
value4 := strings.Map(func(r rune) rune {
return caesar(r, 1)
}, value)
value5 := strings.Map(func(r rune) rune {
return caesar(r, -1)
}, value4)
fmt.Println(value4, value5)
value = "exxegoexsrgi"
result := strings.Map(func(r rune) rune {
return caesar(r, -4)
}, value)
fmt.Println(value, result)
}
//輸出結(jié)果
//test
//lwkl test
//uftu test
//exxegoexsrgi attackatonceAES對(duì)稱(chēng)加密
package main
import (
"bytes"
"crypto/aes"
"fmt"
"testing"
)
//ECB模式解密
func ECBDecrypt(crypted, key []byte) ([]byte, error) {
if !validKey(key) {
return nil, fmt.Errorf("秘鑰長(zhǎng)度錯(cuò)誤,當(dāng)前傳入長(zhǎng)度為 %d",len(key))
}
if len(crypted) < 1 {
return nil, fmt.Errorf("源數(shù)據(jù)長(zhǎng)度不能為0")
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
if len(crypted)%block.BlockSize() != 0 {
return nil, fmt.Errorf("源數(shù)據(jù)長(zhǎng)度必須是 %d 的整數(shù)倍,當(dāng)前長(zhǎng)度為:%d",block.BlockSize(), len(crypted))
}
var dst []byte
tmpData := make([]byte, block.BlockSize())
for index := 0; index < len(crypted); index += block.BlockSize() {
block.Decrypt(tmpData, crypted[index:index+block.BlockSize()])
dst = append(dst, tmpData...)
}
dst, err = PKCS5UnPadding(dst)
if err != nil {
return nil, err
}
return dst, nil
}
//ECB模式加密
func ECBEncrypt(src, key []byte) ([]byte, error) {
if !validKey(key) {
return nil, fmt.Errorf("秘鑰長(zhǎng)度錯(cuò)誤, 當(dāng)前傳入長(zhǎng)度為 %d",len(key))
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
if len(src) < 1 {
return nil, fmt.Errorf("源數(shù)據(jù)長(zhǎng)度不能為0")
}
src = PKCS5Padding(src, block.BlockSize())
if len(src)%block.BlockSize() != 0 {
return nil, fmt.Errorf("源數(shù)據(jù)長(zhǎng)度必須是 %d 的整數(shù)倍,當(dāng)前長(zhǎng)度為:%d",block.BlockSize(), len(src))
}
var dst []byte
tmpData := make([]byte, block.BlockSize())
for index := 0; index < len(src); index += block.BlockSize() {
block.Encrypt(tmpData, src[index:index+block.BlockSize()])
dst = append(dst, tmpData...)
}
return dst, nil
}
// PKCS5填充
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
// 去除PKCS5填充
func PKCS5UnPadding(origData []byte) ([]byte, error) {
length := len(origData)
unpadding := int(origData[length-1])
if length < unpadding {
return nil, fmt.Errorf("invalid unpadding length")
}
return origData[:(length - unpadding)], nil
}
// 秘鑰長(zhǎng)度驗(yàn)證
func validKey(key []byte) bool {
k := len(key)
switch k {
default:
return false
case 16, 24, 32:
return true
}
}
func TestAes(t *testing.T){
srcData := "hello world !"
key := []byte("abcdabcdabcdabcdabcdabcdabcdabcd")
//測(cè)試加密
encData ,err := ECBEncrypt([]byte(srcData),(key))
if err != nil {
t.Errorf(err.Error())
return
}
//測(cè)試解密
decData ,err := ECBDecrypt(encData,key)
if err != nil {
t.Errorf(err.Error())
return
}
t.Log(string(decData))
}到此這篇關(guān)于Golang實(shí)現(xiàn)密碼加密的示例詳解的文章就介紹到這了,更多相關(guān)Golang密碼加密內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang+vue打造高效多語(yǔ)言博客系統(tǒng)的完整指南
這篇文章主要為大家詳細(xì)介紹了如何使用golang和vue打造一個(gè)高效多語(yǔ)言博客系統(tǒng),本文為大家附上了完整版指南,有需要的小伙伴可以參考一下2025-03-03
Golang利用compress/flate包來(lái)壓縮和解壓數(shù)據(jù)
在處理需要高效存儲(chǔ)和快速傳輸?shù)臄?shù)據(jù)時(shí),數(shù)據(jù)壓縮成為了一項(xiàng)不可或缺的技術(shù),Go語(yǔ)言的compress/flate包為我們提供了對(duì)DEFLATE壓縮格式的原生支持,本文將深入探討compress/flate包的使用方法,揭示如何利用它來(lái)壓縮和解壓數(shù)據(jù),并提供實(shí)際的代碼示例,需要的朋友可以參考下2024-08-08
Golang標(biāo)準(zhǔn)庫(kù)之errors包應(yīng)用方式
Go語(yǔ)言的errors包提供了基礎(chǔ)的錯(cuò)誤處理能力,允許通過(guò)errors.New創(chuàng)建自定義error對(duì)象,error在Go中是一個(gè)接口,通過(guò)實(shí)現(xiàn)Error方法來(lái)定義錯(cuò)誤文本,對(duì)錯(cuò)誤的比較通?;趯?duì)象地址,而非文本內(nèi)容,因此即使兩個(gè)錯(cuò)誤文本相同2024-10-10
go語(yǔ)言定時(shí)器Timer及Ticker的功能使用示例詳解
這篇文章主要為大家介紹了go語(yǔ)言定時(shí)器的功能使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04
Golang信號(hào)處理及如何實(shí)現(xiàn)進(jìn)程的優(yōu)雅退出詳解
這篇文章主要給大家介紹了關(guān)于Golang信號(hào)處理及如何實(shí)現(xiàn)進(jìn)程的優(yōu)雅退出的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-03-03

