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密码加密内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • golang+vue打造高效多语言博客系统的完整指南

    golang+vue打造高效多语言博客系统的完整指南

    这篇文章主要为大家详细介绍了如何使用golang和vue打造一个高效多语言博客系统,本文为大家附上了完整版指南,有需要的小伙伴可以参考一下
    2025-03-03
  • Golang利用compress/flate包来压缩和解压数据

    Golang利用compress/flate包来压缩和解压数据

    在处理需要高效存储和快速传输的数据时,数据压缩成为了一项不可或缺的技术,Go语言的compress/flate包为我们提供了对DEFLATE压缩格式的原生支持,本文将深入探讨compress/flate包的使用方法,揭示如何利用它来压缩和解压数据,并提供实际的代码示例,需要的朋友可以参考下
    2024-08-08
  • go语言检测文件是否存在的方法

    go语言检测文件是否存在的方法

    这篇文章主要介绍了go语言检测文件是否存在的方法,实例分析了Go语言文件操作的相关技巧,需要的朋友可以参考下
    2015-03-03
  • go语言中iota和左移右移的使用说明

    go语言中iota和左移右移的使用说明

    这篇文章主要介绍了go语言中iota和左移右移的使用说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05
  • Golang标准库之errors包应用方式

    Golang标准库之errors包应用方式

    Go语言的errors包提供了基础的错误处理能力,允许通过errors.New创建自定义error对象,error在Go中是一个接口,通过实现Error方法来定义错误文本,对错误的比较通常基于对象地址,而非文本内容,因此即使两个错误文本相同
    2024-10-10
  • go语言定时器Timer及Ticker的功能使用示例详解

    go语言定时器Timer及Ticker的功能使用示例详解

    这篇文章主要为大家介绍了go语言定时器的功能使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • Golang最大递减数算法问题分析

    Golang最大递减数算法问题分析

    这篇文章主要介绍了Golang最大递减数算法问题分析,结合实例形式分析了Go语言数字遍历与运算相关操作技巧,需要的朋友可以参考下
    2017-01-01
  • Go中的新增对模糊测试的支持

    Go中的新增对模糊测试的支持

    这篇文章主要为大家介绍了Go中的新增对模糊测试的支持,文中还包含了一些功能实验性测试分析有需要的朋友可以借鉴参考下,希望能够有所帮助
    2022-03-03
  • 详解Go语言的计时器

    详解Go语言的计时器

    Go语言的标准库里提供两种类型的计时器Timer和Ticker。这篇文章主要介绍了Go语言的计时器的相关知识,需要的朋友可以参考下
    2020-05-05
  • Golang信号处理及如何实现进程的优雅退出详解

    Golang信号处理及如何实现进程的优雅退出详解

    这篇文章主要给大家介绍了关于Golang信号处理及如何实现进程的优雅退出的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-03-03

最新评论