go加解密学习笔记
1、概念
加解密分为以下几种
- 对称加密, 加解密都使用的是同一个密钥, 其中的代表就是AES、DES
- 非对加解密, 加解密使用不同的密钥, 其中的代表就是RSA
- 签名算法, 如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))
}
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)]
}
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))
}
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
5.2、创建公钥
这样便生产了密钥。
openssl rsa -in private.pem -pubout -out public.pem
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))
参考链接