Golang实现密码加密的示例详解

 更新时间:2023年07月19日 10:08:56   作者:uccs  
数据库在存储密码时,不能明文存储,需要加密后存储,而Golang中的加密算法有很多种,下面小编就来通过简单的示例和大家简单聊聊吧

数据库在存储密码时,不能明文存储,需要加密后存储

加密算法有很多种,比如:对称加密,非对称加密,哈希算法,密码派生等

在加密之前,我们需要生成一些随机数,这些随机数称为盐

什么是盐?为什么需要盐?

盐是一种加密算法中的一种参数,它是一个随机数,用于增加破解密码的难度

把密码想象成一盘菜,盐就是调料,不加调料就是原汁原味的菜,调料加多加少,菜口味就不一样了

也就是说,盐的多少,会影响到加密后的结果,如果不使用盐,那么相同的密码,加密后的结果是一样的,这样就很容易被破解

盐的生成方式有很多种,比如:随机数,时间戳等

这里用随机数来举例:

  • 定义一个字符串,里面包含了所有可能的字符
  • 根据传入的长度,生成一个切片,并用随机数填充
  • 遍历这个切片,取出随机数,然后对字符串长度取余,得到一个索引,然后把这个索引对应的字符,放到切片中
  • 返回这个切片(这个切片就是盐)
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
}

生成盐之后,我们就可以对密码进行加密了

加密后的密码要使用 hex.EncodeToString 转换成字符串

salt := generateSalt(16)
encoded := pbkdf2.Key([]byte("uccs"), salt, 100, 32, sha256.New)
encodedPwd := hex.EncodeToString(encodedPwd)

因为这个加密算法是不可逆的,也就说你不能通过加密后的密码,反推出原始密码

那用户输入密码时,如何知道密码是否正确呢?

所以在存储加密后的密码时,还要存储盐和加密算法的参数

这里使用的是 pbkdf2 算法,它的参数有:原始密码,盐,迭代次数,密钥长度,哈希算法

所以最终存在数据的密码是:

pwd := fmt.Sprintf("%s$%s$%d$%d$%s", encodedPwd, salt, 100, 32, "pbkdf2-sha256")

这样在验证密码时,就可以通过这些参数,把用户输入的密码,加密后,再和数据库中存储的密码进行比较,如果相同,就说明密码正确

知识补充

当然除了上面的加密方式,Golang中还有很多加密算法的实现,下面小编就来为大家简单整理一下吧,希望对大家有所帮助

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)
}
//输出结果
//test
//lwkl test
//uftu test
//exxegoexsrgi attackatonce

AES对称加密

package main
import (
	"bytes"
	"crypto/aes"
	"fmt"
	"testing"
)
//ECB模式解密
func ECBDecrypt(crypted, key []byte) ([]byte, error) {
	if !validKey(key) {
		return nil, fmt.Errorf("秘钥长度错误,当前传入长度为 %d",len(key))
	}
	if len(crypted) < 1 {
		return nil, fmt.Errorf("源数据长度不能为0")
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	if len(crypted)%block.BlockSize() != 0 {
		return nil, fmt.Errorf("源数据长度必须是 %d 的整数倍,当前长度为:%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("秘钥长度错误, 当前传入长度为 %d",len(key))
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	if len(src) < 1 {
		return nil, fmt.Errorf("源数据长度不能为0")
	}
	src = PKCS5Padding(src, block.BlockSize())
	if len(src)%block.BlockSize() != 0 {
		return nil, fmt.Errorf("源数据长度必须是 %d 的整数倍,当前长度为:%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
}
// 秘钥长度验证
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")
	//测试加密
	encData ,err := ECBEncrypt([]byte(srcData),(key))
	if err != nil {
		t.Errorf(err.Error())
		return
	}
	//测试解密
	decData ,err := ECBDecrypt(encData,key)
	if err != nil {
		t.Errorf(err.Error())
		return
	}
	t.Log(string(decData))
}

到此这篇关于Golang实现密码加密的示例详解的文章就介绍到这了,更多相关Golang密码加密内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go语言实现超时的三种方法实例

    Go语言实现超时的三种方法实例

    超时在一些业务场景里非常普遍,下面这篇文章主要给大家介绍了关于Go语言实现超时的三种方法,文中通过实例代码介绍的非常详细,对大家学习或者使用Go语言具有一定的参考学习价值,需要的朋友可以参考下
    2022-07-07
  • MacOS中 VSCode 安装 GO 插件失败问题的快速解决方法

    MacOS中 VSCode 安装 GO 插件失败问题的快速解决方法

    这篇文章主要介绍了MacOS中 VSCode 安装 GO 插件失败问题的快速解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • go中的时间处理过程

    go中的时间处理过程

    这篇文章主要介绍了go中的时间处理过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-07-07
  • go语言中基本数据类型及应用快速了解

    go语言中基本数据类型及应用快速了解

    这篇文章主要为大家介绍了go语言中基本数据类型应用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • go-zero数据的流处理利器fx使用详解

    go-zero数据的流处理利器fx使用详解

    这篇文章主要为大家介绍了go-zero数据的流处理利器fx使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • Golang实现组合模式和装饰模式实例详解

    Golang实现组合模式和装饰模式实例详解

    这篇文章主要介绍了Golang实现组合模式和装饰模式,本文介绍组合模式和装饰模式,golang实现两种模式有共同之处,但在具体应用场景有差异。通过对比两个模式,可以加深理解,需要的朋友可以参考下
    2022-11-11
  • Golang中Retry重试实践

    Golang中Retry重试实践

    本文主要介绍了Golang中Retry重试实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-02-02
  • GO实现协程池管理的方法

    GO实现协程池管理的方法

    这篇文章给大家介绍GO实现协程池管理的方法,分别使用channel实现协程池和消费者模式实现协程池,本文通过实例代码给大家介绍的非常详细,需要的朋友参考下吧
    2021-07-07
  • Golang Gin框架实现多种数据格式返回结果详解

    Golang Gin框架实现多种数据格式返回结果详解

    这篇文章主要介绍了Golang Gin框架实现多种数据格式返回结果,我们都知道,一个完整的请求包含请求和处理请求以及结果返回三个步骤,在服务器端对请求处理完成以后,会将结果返回给客户端,在gin框架中,支持返回多种请求数据格式,下面我们一起来看看
    2023-05-05
  • golang中使用aes加密的过程

    golang中使用aes加密的过程

    AES(Advanced Encryption Standard)是一种对称加密算法,适用于加密敏感数据,本文给大家介绍golang中使用aes加密的相关知识,感兴趣的朋友跟随小编一起看看吧
    2025-11-11

最新评论