go加解密学习笔记

1、概念

加解密分为以下几种

  1. 对称加密, 加解密都使用的是同一个密钥, 其中的代表就是AES、DES
  2. 非对加解密, 加解密使用不同的密钥, 其中的代表就是RSA
  3. 签名算法, 如MD5、SHA1、HMAC等, 主要用于验证,防止信息被修改, 如:文件校验、数字签名、鉴权协议

Base64不是加密算法,它是一种数据编码方式,虽然是可逆的,但是它的编码方式是公开的,无所谓加密。本文也对Base64编码方式做了简要介绍

2、AES

AES,即高级加密标准(Advanced Encryption Standard),是一个对称分组密码算法,旨在取代DES成为广泛使用的标准。AES中常见的有三种解决方案,分别为AES-128、AES-192和AES-256。
AES加密过程涉及到4种操作:字节替代(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。解密过程分别为对应的逆操作。由于每一步操作都是可逆的,按照相反的顺序进行解密即可恢复明文。加解密中每轮的密钥分别由初始密钥扩展得到。算法中16字节的明文、密文和轮密钥都以一个4x4的矩阵表示。
AES 有五种加密模式

电码本模式(Electronic Codebook Book (ECB))
密码分组链接模式(Cipher Block Chaining (CBC))
计算器模式(Counter (CTR))
密码反馈模式(Cipher FeedBack (CFB))
输出反馈模式(Output FeedBack (OFB))

2.1、ECB模式

出于安全考虑,golang默认并不支持ECB模式。

package main

//ECB模式
import (
   "crypto/aes"
   "fmt"
)

func AESEncrypt(src []byte, key []byte) (encrypted []byte) {
   cipher, _ := aes.NewCipher(generateKey(key))
   length := (len(src) + aes.BlockSize) / aes.BlockSize
   plain := make([]byte, length*aes.BlockSize)
   copy(plain, src)
   pad := byte(len(plain) - len(src))
   for i := len(src); i < len(plain); i++ {
      plain[i] = pad
   }
   encrypted = make([]byte, len(plain))
   // 分组分块加密
   for bs, be := 0, cipher.BlockSize(); bs <= len(src); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
      cipher.Encrypt(encrypted[bs:be], plain[bs:be])
   }

   return encrypted
}

func AESDecrypt(encrypted []byte, key []byte) (decrypted []byte) {
   cipher, _ := aes.NewCipher(generateKey(key))
   decrypted = make([]byte, len(encrypted))
   //
   for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
      cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
   }

   trim := 0
   if len(decrypted) > 0 {
      trim = len(decrypted) - int(decrypted[len(decrypted)-1])
   }

   return decrypted[:trim]
}

func generateKey(key []byte) (genKey []byte) {
   genKey = make([]byte, 16)
   copy(genKey, key)
   for i := 16; i < len(key); {
      for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
         genKey[j] ^= key[i]
      }
   }
   return genKey
}
func main() {

   source := "hello world"
   fmt.Println("原字符:", source)
   //16byte密钥
   key := "1443flfsaWfdas"
   encryptCode := AESEncrypt([]byte(source), []byte(key))
   fmt.Println("密文:", string(encryptCode))

   decryptCode := AESDecrypt(encryptCode, []byte(key))

   fmt.Println("解密:", string(decryptCode))
}

image-20230206214458701

2.2、CBC模式

package main

import (
   "bytes"
   "crypto/aes"
   "crypto/cipher"
   "encoding/base64"
   "fmt"
)

func main() {
   orig := "hello world"
   key := "0123456789012345"
   fmt.Println("原文:", orig)
   encryptCode := AesEncrypt(orig, key)
   fmt.Println("密文:", encryptCode)
   decryptCode := AesDecrypt(encryptCode, key)
   fmt.Println("解密结果:", decryptCode)
}
func AesEncrypt(orig string, key string) string {
   // 转成字节数组
   origData := []byte(orig)
   k := []byte(key)
   // 分组秘钥
   // NewCipher该函数限制了输入k的长度必须为16, 24或者32
   block, _ := aes.NewCipher(k)
   // 获取秘钥块的长度
   blockSize := block.BlockSize()
   // 补全码
   origData = PKCS7Padding(origData, blockSize)
   // 加密模式
   blockMode := cipher.NewCBCEncrypter(block, k[:blockSize])
   // 创建数组
   cryted := make([]byte, len(origData))
   // 加密
   blockMode.CryptBlocks(cryted, origData)
   return base64.StdEncoding.EncodeToString(cryted)
}
func AesDecrypt(cryted string, key string) string {
   // 转成字节数组
   crytedByte, _ := base64.StdEncoding.DecodeString(cryted)
   k := []byte(key)
   // 分组秘钥
   block, _ := aes.NewCipher(k)
   // 获取秘钥块的长度
   blockSize := block.BlockSize()
   // 加密模式
   blockMode := cipher.NewCBCDecrypter(block, k[:blockSize])
   // 创建数组
   orig := make([]byte, len(crytedByte))
   // 解密
   blockMode.CryptBlocks(orig, crytedByte)
   // 去补全码
   orig = PKCS7UnPadding(orig)
   return string(orig)
}

//补码
//AES加密数据块分组长度必须为128bit(byte[16]),密钥长度可以是128bit(byte[16])、192bit(byte[24])、256bit(byte[32])中的任意一个。
func PKCS7Padding(ciphertext []byte, blocksize int) []byte {
   padding := blocksize - len(ciphertext)%blocksize
   padtext := bytes.Repeat([]byte{byte(padding)}, padding)
   return append(ciphertext, padtext...)
}

//去码
func PKCS7UnPadding(origData []byte) []byte {
   length := len(origData)
   unpadding := int(origData[length-1])
   return origData[:(length - unpadding)]
}

image-20230206214702343

2.3、CRT模式

package main

import (
   "crypto/aes"
   "crypto/cipher"
   "fmt"
)

//AEC加密和解密(CRT模式)
func aesCtrCrypt(plainText []byte, key []byte) ([]byte, error) {

   //指定加密、解密算法为AES,返回一个AES的Block接口对象
   block, err := aes.NewCipher(key)
   if err != nil {
      panic(err)
   }
   //指定计数器,长度必须等于block的块尺寸
   count := []byte("12345678abcdefgh")
   //指定分组模式
   blockMode := cipher.NewCTR(block, count)
   //执行加密、解密操作
   message := make([]byte, len(plainText))
   blockMode.XORKeyStream(message, plainText)
   
   //返回明文或密文
   return message, nil
}

func main() {
   source := "hello world"
   fmt.Println("原字符:", source)

   key := "1234567812345678"
   encryptCode, _ := aesCtrCrypt([]byte(source), []byte(key))
   fmt.Println("密文:", string(encryptCode))

   decryptCode, _ := aesCtrCrypt(encryptCode, []byte(key))

   fmt.Println("解密:", string(decryptCode))
}

2.4、CFB模式

package main

import (
   "crypto/aes"
   "crypto/cipher"
   "crypto/rand"
   "encoding/hex"
   "fmt"
   "io"
)

func AesEncryptCFB(origData []byte, key []byte) (encrypted []byte) {
   block, err := aes.NewCipher(key)
   if err != nil {
      //panic(err)
      fmt.Println("err:", err)
   }
   encrypted = make([]byte, aes.BlockSize+len(origData))
   iv := encrypted[:aes.BlockSize]
   if _, err := io.ReadFull(rand.Reader, iv); err != nil {
      //panic(err)
   }
   stream := cipher.NewCFBEncrypter(block, iv)
   stream.XORKeyStream(encrypted[aes.BlockSize:], origData)
   return encrypted
}
func AesDecryptCFB(encrypted []byte, key []byte) (decrypted []byte) {
   block, _ := aes.NewCipher(key)
   if len(encrypted) < aes.BlockSize {
      panic("ciphertext too short")
   }
   iv := encrypted[:aes.BlockSize]
   encrypted = encrypted[aes.BlockSize:]

   stream := cipher.NewCFBDecrypter(block, iv)
   stream.XORKeyStream(encrypted, encrypted)
   return encrypted
}
func main() {
   source := "hello world"
   fmt.Println("原字符:", source)
   key := "ABCDEFGHIJKLMNO1" //16位
   encryptCode := AesEncryptCFB([]byte(source), []byte(key))
   fmt.Println("密文:", hex.EncodeToString(encryptCode))
   decryptCode := AesDecryptCFB(encryptCode, []byte(key))

   fmt.Println("解密:", string(decryptCode))
}

image-20230206214824368

2.5、OFB模式

package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
)

func aesEncryptOFB(data []byte, key []byte) ([]byte, error) {
	data = PKCS7Padding(data, aes.BlockSize)
	block, _ := aes.NewCipher([]byte(key))
	out := make([]byte, aes.BlockSize+len(data))
	iv := out[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return nil, err
	}

	stream := cipher.NewOFB(block, iv)
	stream.XORKeyStream(out[aes.BlockSize:], data)
	return out, nil
}

func aesDecryptOFB(data []byte, key []byte) ([]byte, error) {
	block, _ := aes.NewCipher([]byte(key))
	iv := data[:aes.BlockSize] //aes.BlockSize=16 偏移量
	data = data[aes.BlockSize:]
	if len(data)%aes.BlockSize != 0 {
		return nil, fmt.Errorf("data is not a multiple of the block size")
	}

	out := make([]byte, len(data))
	mode := cipher.NewOFB(block, iv)
	mode.XORKeyStream(out, data)

	out = PKCS7UnPadding(out)
	return out, nil
}

//补码
//AES加密数据块分组长度必须为128bit(byte[16]),密钥长度可以是128bit(byte[16])、192bit(byte[24])、256bit(byte[32])中的任意一个。
func PKCS7Padding(ciphertext []byte, blocksize int) []byte {
	padding := blocksize - len(ciphertext)%blocksize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

//去码
func PKCS7UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}
func main() {
	source := "hello world"
	fmt.Println("原字符:", source)
	key := "1111111111111111" //16位  32位均可
	encryptCode, _ := aesEncryptOFB([]byte(source), []byte(key))
	fmt.Println("密文:", hex.EncodeToString(encryptCode))
	decryptCode, _ := aesDecryptOFB(encryptCode, []byte(key))

	fmt.Println("解密:", string(decryptCode))
}

3、DES

DES是一种对称加密算法,又称为美国数据加密标准。DES加密时以64位分组对数据进行加密,加密和解密都使用的是同一个长度为64位的密钥,实际上只用到了其中的56位,密钥中的第8、16…64位用来作奇偶校验。DES有ECB(电子密码本)和CBC(加密块)等加密模式。DES算法的安全性很高,目前除了穷举搜索破解外, 尚无更好的的办法来破解。其密钥长度越长,破解难度就越大。
填充和去填充函数。

本代码采用CBC加密模式,填充方式采用PKCS5Padding

package main

import (
	"bytes"
	"crypto/cipher"
	"crypto/des"
	"encoding/base64"
	"fmt"
)

//DES加密方法
func MyDESEncrypt(origData, key []byte) {
	//将字节秘钥转换成block快
	block, _ := des.NewCipher(key)
	//对明文先进行补码操作
	origData = PKCS5Padding(origData, block.BlockSize())
	//设置加密方式
	blockMode := cipher.NewCBCEncrypter(block, key)
	//创建明文长度的字节数组
	crypted := make([]byte, len(origData))
	//加密明文,加密后的数据放到数组中
	blockMode.CryptBlocks(crypted, origData)
	//将字节数组转换成字符串
	fmt.Println(base64.StdEncoding.EncodeToString(crypted))

}

//实现明文的补码
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	//计算出需要补多少位
	padding := blockSize - len(ciphertext)%blockSize
	//Repeat()函数的功能是把参数一 切片复制 参数二count个,然后合成一个新的字节切片返回
	// 需要补padding位的padding值
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	//把补充的内容拼接到明文后面
	return append(ciphertext, padtext...)
}

//解密
func MyDESDecrypt(data string, key []byte) {
	//倒叙执行一遍加密方法
	//将字符串转换成字节数组
	crypted, _ := base64.StdEncoding.DecodeString(data)
	//将字节秘钥转换成block快
	block, _ := des.NewCipher(key)
	//设置解密方式
	blockMode := cipher.NewCBCDecrypter(block, key)
	//创建密文大小的数组变量
	origData := make([]byte, len(crypted))
	//解密密文到数组origData中
	blockMode.CryptBlocks(origData, crypted)
	//去补码
	origData = PKCS5UnPadding(origData)
	//打印明文
	fmt.Println(string(origData))
}

//去除补码
func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	// 去掉最后一个字节 unpadding 次
	unpadding := int(origData[length-1])
	//解密去补码时需取最后一个字节,值为m,则从数据尾部删除m个字节,剩余数据即为加密前的原文
	return origData[:(length - unpadding)]
}

func main() {
	//定义明文
	data := []byte("hello world")
	//密钥
	key := []byte("12345678")
	//加密
	MyDESEncrypt(data, key)
	//解密
	MyDESDecrypt("CyqS6B+0nOGkMmaqyup7gQ==", key)
}

4、3DES

对比DES,3DES只是换了NewTripleDESCipher。不过,需要注意的是,密钥长度必须24byte

package main

import (
   "bytes"
   "crypto/cipher"
   "crypto/des"
   "fmt"
)

//DES 和 3DES加密区别
//前者 加密  密钥必须是8byte
//后者加密 解密 再加密  密钥必须是24byte
func main() {
   //定义密钥,必须是24byte
   key := []byte("123456789012345678901234")
   //定义明文
   origData := []byte("hello world")

   //加密
   en := ThriDESEnCrypt(origData, key)
   fmt.Println("密文:", string(en))
   //解密
   de := ThriDESDeCrypt(en, key)
   fmt.Println("解密:", string(de))
}

//解密
func ThriDESDeCrypt(crypted, key []byte) []byte {
   //获取block块
   block, _ := des.NewTripleDESCipher(key)
   //创建切片
   context := make([]byte, len(crypted))

   //设置解密方式
   blockMode := cipher.NewCBCDecrypter(block, key[:8])
   //解密密文到数组
   blockMode.CryptBlocks(context, crypted)

   //去补码
   context = PKCSUnPadding(context)
   return context
}

//去补码
func PKCSUnPadding(origData []byte) []byte {
   length := len(origData)
   unpadding := int(origData[length-1])
   return origData[:length-unpadding]
}

//加密
func ThriDESEnCrypt(origData, key []byte) []byte {
   //获取block块
   block, _ := des.NewTripleDESCipher(key)
   //补码
   origData = PKCSPadding(origData, block.BlockSize())
   //设置加密方式为 3DES  使用3条56位的密钥对数据进行三次加密
   blockMode := cipher.NewCBCEncrypter(block, key[:8])

   //创建明文长度的数组
   crypted := make([]byte, len(origData))

   //加密明文
   blockMode.CryptBlocks(crypted, origData)

   return crypted

}

//补码
func PKCSPadding(origData []byte, blockSize int) []byte {
   //计算需要补几位数
   padding := blockSize - len(origData)%blockSize
   //在切片后面追加char数量的byte(char)
   padtext := bytes.Repeat([]byte{byte(padding)}, padding)

   return append(origData, padtext...)
}

5、RSA

这是一个非对称加密算法,一般通过公钥加密,私钥解密。在加解密过程中,使用openssl生产密钥。执行如下操作:

5.1、创建秘钥

密钥长度,1024觉得不够安全的话可以用2048,但是代价也相应增大

openssl genrsa -out private.pem 1024  

image-20230206225109672

5.2、创建公钥

这样便生产了密钥。

openssl rsa -in private.pem -pubout -out public.pem 

image-20230206225135029

package main

import (
   "crypto/rand"
   "crypto/rsa"
   "crypto/x509"
   "encoding/pem"
   "errors"
   "fmt"
)

const privateKey = "-----BEGIN RSA PRIVATE KEY-----\nMIICXQIBAAKBgQD*****************rIfZtAscC/4/Nt4\n-----END RSA PRIVATE KEY-----"//修改为自己的私钥

const publicKey = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCyO17QHp25mr*****************************lq+Oa5fRNQIDAQAB\n-----END PUBLIC KEY-----"//修改为自己的公钥

//加密
func RsaEncrypt(origData []byte) ([]byte, error) {
   block, _ := pem.Decode([]byte(publicKey))
   if block == nil {
      return nil, errors.New("public key error")
   }
   pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
   if err != nil {
      return nil, err
   }
   pub := pubInterface.(*rsa.PublicKey)
   return rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
}

//解密
func RsaDecrypt(ciphertext []byte) ([]byte, error) {
   block, _ := pem.Decode([]byte(privateKey))
   if block == nil {
      return nil, errors.New("private key error!")
   }
   priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
   if err != nil {
      return nil, err
   }
   return rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)
}
func main() {
   data, err := RsaEncrypt([]byte("hello world"))
   if err != nil {
      panic(err)
   }
   origData, err := RsaDecrypt(data)
   if err != nil {
      panic(err)
   }
   fmt.Println(string(origData))
}

6、MD5

MD5的全称是Message-DigestAlgorithm 5,可以产生出一个128位(16进制,32个字符)的散列值(hash value),用于确保信息传输完整一致

func GetMd5String(s string) string {
   h := md5.New()
   h.Write([]byte(s))
   return hex.EncodeToString(h.Sum(nil))
}

func main() {
   text := GetMd5String("hello word")
   fmt.Println(text)
}

7、sha1

SHA-1可以生成一个被称为消息摘要的160(20字节)散列值,散列值通常的呈现形式为40个十六进制数。

func GetSha1String(s string) string {
   //产生一个散列值得方式是 sha1.New(),sha1.Write(bytes),然后 sha1.Sum([]byte{})。这里我们从一个新的散列开始。
   h := sha1.New()
   //写入要处理的字节。如果是一个字符串,需要使用[]byte(s) 来强制转换成字节数组。
   h.Write([]byte(s))
   //这个用来得到最终的散列值的字符切片。Sum 的参数可以用来都现有的字符切片追加额外的字节切片:一般不需要要。
   bs := h.Sum(nil)
   //SHA1 值经常以 16 进制输出,例如在 git commit 中。使用%x 来将散列结果格式化为 16 进制字符串。

   return hex.EncodeToString(bs)

}

func main() {
   text := GetSha1String("hello word")
   fmt.Println(text)
}

8、bcrypt

需要先下载该包(已配置goproxy和gomodule)

go get golang.org/x/crypto/bcrypt
package main

import (
	"fmt"
	"golang.org/x/crypto/bcrypt"
)

func main() {
	password := "test"
	hash, _ := bcrypt.GenerateFromPassword([]byte(password), 0)
	fmt.Println("密文:", string(hash))

	// 密码如果校验成功会返回Nil
	fmt.Println(bcrypt.CompareHashAndPassword(hash, []byte("test")))
}

9、hmac

HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code)的缩写,

它通过一个标准算法,在计算哈希的过程中,把key混入计算过程中。

和我们自定义的加salt算法不同,Hmac算法针对所有哈希算法都通用,无论是MD5还是SHA-1。采用Hmac替代我们自己的salt算法,可以使程序算法更标准化,也更安全。

//key随意设置 data 要加密数据
func Hmac(key, data string) string {
   hash := hmac.New(md5.New, []byte(key)) // 创建对应的md5哈希加密算法
   hash.Write([]byte(data))
   return hex.EncodeToString(hash.Sum([]byte("")))
}
func HmacSha256(key, data string) string {
   hash := hmac.New(sha256.New, []byte(key)) //创建对应的sha256哈希加密算法
   hash.Write([]byte(data))
   return hex.EncodeToString(hash.Sum([]byte("")))
}

func main() {

   key := "123456"
   data := GetMd5String("text")
   fmt.Println("加salt前:", data)
   en := Hmac(key, data)
   fmt.Println("加salt1后:", en)
   en1 := HmacSha256(key, data)
   fmt.Println("加salt2后:", en1)
}

10、base64

Base64是一种任意二进制到文本字符串的编码方法,常用于在URL、Cookie、网页中传输少量二进制数据。
首先使用Base64编码需要一个含有64个字符的表,这个表由大小写字母、数字、+和/组成。采用Base64编码处理数据时,会把每三个字节共24位作为一个处理单元,再分为四组,每组6位,查表后获得相应的字符即编码后的字符串。编码后的字符串长32位,这样,经Base64编码后,原字符串增长1/3。如果要编码的数据不是3的倍数,最后会剩下一到两个字节,Base64编码中会采用\x00在处理单元后补全,编码后的字符串最后会加上一到两个 = 表示补了几个字节。

const (
   base64Table = "IJjkKLMNO567PQX12RVW3YZaDEFGbcdefghiABCHlSTUmnopqrxyz04stuvw89+/"
)

var coder = base64.NewEncoding(base64Table)

func Base64Encode(src []byte) []byte { //编码
   return []byte(coder.EncodeToString(src))
}

func Base64Decode(src []byte) ([]byte, error) { //解码
   return coder.DecodeString(string(src))
}

func main() {
   en := Base64Encode([]byte("hello world"))
   fmt.Println(string(en))
   text, _ := Base64Decode(en)
   fmt.Println(string(text))

image-20230206233017255

参考链接

https://blog.csdn.net/wade3015/article/details/84454836

https://www.cnblogs.com/you-men/p/14160439.html

posted @ 2023-02-06 23:41  akka1  阅读(303)  评论(0编辑  收藏  举报