1 对称加密
1 简介
对称加密,及使用相同的公钥和私钥对数据进行加密和解密操作,其确保了加密的每一步都是可逆的,其中最简单的对称加密算法就是异或
2 异或实例
package main
import (
"fmt"
"math/rand"
)
// 简单的加密算法,使用一串byte进行与操作
func Xor() {
plain := []byte("hello") //明文
key := make([]byte, len(plain))
for i := 0; i < len(key); i++ {
key[i] = byte(rand.Intn(10)) // 随机数key
}
cipher := make([]byte, len(plain))
// 加密
for i, ele := range plain {
cipher[i] = ele ^ key[i] // 异或,不同为True ,相同为False
}
fmt.Println("密文如下:", string(cipher))
// 解密
rest := make([]byte, len(cipher))
for i, ele := range cipher {
rest[i] = ele ^ key[i]
}
fmt.Println("明文如下:", string(rest))
}
func main() {
Xor()
}
3 DES 数据分组
1 简介
DES(Data Encryption Standard) 数据加密标准,是目前最流行的加密算法之一
其对原始数据(明文)进行分组,每组64位,最后一组不足64位时按一定规则进行填充
每组上单独施加DES 算法
2 DES 子秘钥生成过程
初始秘钥64位,由使用者自己提供,也用于解密过程,每隔7位有一个校验位,其根据初始秘钥生成16个48位子秘钥
1 初始的64位秘钥,通过公开的秘钥置换表,生成56位的密文
2 56位密文通过分为左28位和右28位进行旋转,而后进行合并,形成新的56位秘钥表。
3 通过置换最后生成48位的子秘钥
4 加密过程
1 S盒替换
输入48位,输出32位,各分为8组,每组6位,输出每组4位,分别在每组上施加S盒替换,一共8个S盒
2 分组模式
CBC(cipher block chaining)密文分组连接模式,将当前的明文分组并与前面的密文进行异或运算,然后再进行加密,其他的分组模式还有ECB,CTR,CFR,OFB等
3 加密过程
1 明文通过初始置换,形成L0(32位)和 R0(32位)
2 R0(32位)通过扩展置换生成48位,48位通过和上述生成的子秘钥进行异或操作,生成48位的密文
3 48位密文通过S盒替换生成32位密文
4 32位密文通过P和替换生成32位密文
5 32位密文通过盒L0进行异或,最终形成32位R1 的密文
6 通过上述的循环,获取到L16和R16
7 通过对L16和R16 的合并,通过最终置换形成64位密文
5 详解
1 DES加密
1 加密过程
1 通过des.NewCipher(key) 创建加密key 64位
2 通过block.BlockSize() 获取分组大小,默认是8位
3 通过自定义方法对加密数据中不足8位的进行填充
4 通过 block.Encrypt(dst,src[:blockSize]) 输出密文信息
5 通过 hex.EncodeToString(out) 进行最终数据输出
2 解密过程
1 通过des.NewCipher(key) 创建加密key 64位
2 通过block.BlockSize() 获取分组大小,默认是8位
3 通过 block.Decrypt(dst,src[:blockSize]) 输出明文信息
4 通过自定义方法进行反填充
5 通过string(out) 转换为字符串输出
3 代码
package main
import (
"bytes"
"crypto/des"
"encoding/hex"
"fmt"
"log"
)
// ciphertxt 密文数据,blockSize 填充长度
func ZeroPadding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize // 需要填充的字节数 8 5%8 //最终为5 ,则需要填充3位,明文填充后才能被使用,否则key 存在问题
padtext := bytes.Repeat([]byte{0}, padding+blockSize) // 使用0 进行填充,多填充一位,8字节
rect := append(ciphertext, padtext...) // 填充并加入
rect[len(rect)-1] = byte(padding + blockSize) // 填充的字节数量
return rect
}
func ZeroUnPadding(origData []byte) []byte {
padding := origData[len(origData)-1] // 取出最后一位的记录数据
return origData[0 : len(origData)-int(padding)] // 进行去除最后一位
}
// Des 加密,密钥必须是64位,因此key 的长度为8位的byte数组
func DesEncrypt(text string, key []byte) (string, error) {
src := []byte(text) // 明文进行操作
block, err := des.NewCipher(key) //创建一个加密key,64位
if err != nil {
return "", err
}
blockSize := block.BlockSize() //分组大小
src = ZeroPadding(src, blockSize) // 进行填充,最终是形成8的整数倍的数据
out := make([]byte, len(src))
dst := out
for len(src) > 0 {
// 分组加密
block.Encrypt(dst, src[:blockSize]) // 对 src 的 8 位进行加密,结果放入dst中
src = src[blockSize:] //移位操作
dst = dst[blockSize:]
}
return hex.EncodeToString(out), nil // 转换成string
}
//DesDecrypt DES解密
//密钥必须是64位,所以key必须是长度为8的byte数组
func DesDecrypt(text string, key []byte) (string, error) {
src, err := hex.DecodeString(text) //转成[]byte
if err != nil {
return "", err
}
block, err := des.NewCipher(key)
if err != nil {
return "", err
}
blockSize := block.BlockSize()
out := make([]byte, len(src))
dst := out
for len(src) > 0 {
//分组解密
block.Decrypt(dst, src[:blockSize])
src = src[blockSize:]
dst = dst[blockSize:]
}
out = ZeroUnPadding(out) //反填充
return string(out), nil
}
func main() {
key := []byte("12345678") //key必须是长度为8的byte数组
plain := "Golang 对称加密测试!!!"
cipher, err := DesEncrypt(plain, key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("密文:%s\n", cipher)
}
2 CBC 加密
1 加密过程
1 aes.BlockSize 获取分组大小
2 aes.NewCipher(key) 创建key分组
3 通过 cipher.NewCBCEncrypter(block, key) 指定分组加密模式
4 加密数据 encrypter.CryptBlocks(encrypted, src)
5 通过hex.EncodeToString(encrypted) 转换加密数据并输出
2 解密过程
1 通过 hex.DecodeString(text) 转换为 []byte 数据
2 通过aes.NewCipher(key) 创建key分组
3 通过cipher.NewCBCDecrypter(block, key)指定解密模式
4 通过 encrypter.CryptBlocks(encrypted, src) 进行数据解密
5 转换为string 输出
3 代码
package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"fmt"
"log"
)
// ciphertxt 密文数据,blockSize 填充长度
func ZeroPadding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize // 需要填充的字节数 8 5%8 //最终为5 ,则需要填充3位,明文填充后才能被使用,否则key 存在问题
padtext := bytes.Repeat([]byte{0}, padding+blockSize) // 使用0 进行填充,多填充一位,8字节
rect := append(ciphertext, padtext...) // 填充并加入
rect[len(rect)-1] = byte(padding + blockSize) // 填充的字节数量
return rect
}
func ZeroUnPadding(origData []byte) []byte {
padding := origData[len(origData)-1] // 取出最后一位的记录数据
return origData[0 : len(origData)-int(padding)] // 进行去除最后一位
}
func AesEncrypt(text string, key []byte) (string, error) {
blockSize := aes.BlockSize // AES 的分组大小
src := []byte(text)
src = ZeroPadding(src, blockSize)
encrypted := make([]byte, len(src))
block, err := aes.NewCipher(key) //创建加密key
if err != nil {
return "", err
}
encrypter := cipher.NewCBCEncrypter(block, key) //CBC 分组模式加密
encrypter.CryptBlocks(encrypted, src) // 对多个块进行加密
return hex.EncodeToString(encrypted), nil
}
func AesDecrypt(text string, key []byte) (string, error) {
src, err := hex.DecodeString(text)
if err != nil {
return "", err
}
decrypted := make([]byte, len(src))
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
edecrypter := cipher.NewCBCDecrypter(block, key) //CBC分组模式解密
edecrypter.CryptBlocks(decrypted, src)
out := ZeroUnPadding(decrypted) //反填充
return string(out), nil
}
func main() {
key := []byte("1234567812345678") //key必须是长度为8的byte数组
plain := "Golang 对称加密测试!!!"
cipher, err := AesEncrypt(plain, key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("密文:%s\n", cipher)
}
2 非对称加密
1 简介
使用公钥加密,使用私钥解密,其公钥和私钥不同,公钥可以公布给所有人,私钥自己持有,相对对称加密,非对称加密速度更慢
2 RSA
1 简介
Ron Rivest,Adi Shamir,Leonard Adleman ,秘钥越长,越难破解,目前768位的秘钥还无法破解,因此可以认为1024位的RSA 秘钥基本安全,2048位的秘钥非常安全,RSA 的算法原理使用了数论
2 加密过程
1 随机选择两个不相等的质数p和q
2 计算p和q的乘积n
3 计算n 的欧拉函数
4 随机选择一个整数e,使得1<e<上述值,切e和上述第三部获取的值互为质数
5 计算e对第三部获取的数值的模反元素d
6 将n和e封装成公钥,n和d 封装成私钥
3 椭圆曲线加密
1 简介
ECC(Elliptic Curve Cryptography)椭圆曲线加密算法,相比RSA,ECC 可以使用更短的秘钥,来实现与RSA相当或更高的安全
定义了椭圆曲线上的加法和二倍数
椭圆曲线依赖的数学难以是:k为正整数,P是椭圆曲线上的点,成为基点,K*P=Q,已知Q和P,很难计算出k
3 加密代码
2 RSA 加密
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"os"
)
/**
生成1024位的RSA私钥:
openssl genrsa -out data/rsa_private_key.pem 1024
根据私钥生成公钥:
openssl rsa -in data/rsa_private_key.pem -pubout -out data/rsa_public_key.pem
pem是一种标准格式,它通常包含页眉和页脚
*/
var (
publicKey []byte
privateKey []byte
)
func ReadFile(keyFile string) ([]byte, error) {
if f, err := os.Open(keyFile); err != nil {
return nil, err
} else {
content := make([]byte, 4096)
if n, err := f.Read(content); err != nil {
return nil, err
} else {
return content[:n], nil
}
}
}
func ReadRSAKey(publicKeyFile, privateKeyFile string) (err error) {
if publicKey, err = ReadFile(publicKeyFile); err != nil {
return err
}
if privateKey, err = ReadFile(privateKeyFile); err != nil {
return err
}
return
}
// RSA加密
func RsaEncrypt(origData []byte) ([]byte, error) {
//解密pem格式的公钥
block, _ := pem.Decode(publicKey)
if block == nil {
return nil, errors.New("public key error")
}
// 解析公钥
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) //目前的数字证书一般都是基于ITU(国际电信联盟)制定的X.509标准
if err != nil {
return nil, err
}
// 类型断言
pub := pubInterface.(*rsa.PublicKey)
//加密
return rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
}
// RSA解密
func RsaDecrypt(ciphertext []byte) ([]byte, error) {
//解密
block, _ := pem.Decode(privateKey)
if block == nil {
return nil, errors.New("private key error")
}
//解析PKCS1格式的私钥
privInf, err := x509.ParsePKCS1PrivateKey(block.Bytes)
// ParsePPKCS1PrivateKey
if err != nil {
return nil, err
}
// 解密
return rsa.DecryptPKCS1v15(rand.Reader, privInf, ciphertext)
}
func main() {
ReadRSAKey("data/rsa_public_key.pem", "data/rsa_private_key.pem")
plain := "GOlang 测试!!!"
cipher, err := RsaEncrypt([]byte(plain))
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("密文:%s\n", hex.EncodeToString(cipher))
// fmt.Printf("密文:%v\n", (cipher))
bPlain, err := RsaDecrypt(cipher)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("明文:%s\n", string(bPlain))
}
}
}
3 ECC 加解密
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"fmt"
"log"
"github.com/ethereum/go-ethereum/crypto/ecies" //以太坊加密库,要求go版本升级到1.15
)
func genPrivateKey() (*ecies.PrivateKey, error) {
pubkeyCurve := elliptic.P256() // 初始化椭圆曲线
// 随机挑选基点,生成私钥
p, err := ecdsa.GenerateKey(pubkeyCurve, rand.Reader) //用golang标准库生成公私钥对
if err != nil {
return nil, err
} else {
return ecies.ImportECDSA(p), nil //转换成以太坊的公私钥对
}
}
//ECCEncrypt 椭圆曲线加密
func ECCEncrypt(plain string, pubKey *ecies.PublicKey) ([]byte, error) {
src := []byte(plain)
return ecies.Encrypt(rand.Reader, pubKey, src, nil, nil)
}
//ECCDecrypt 椭圆曲线解密
func ECCDecrypt(cipher []byte, prvKey *ecies.PrivateKey) (string, error) {
if src, err := prvKey.Decrypt(cipher, nil, nil); err != nil {
return "", err
} else {
return string(src), nil
}
}
func main() {
prvKey, err := genPrivateKey()
if err != nil {
log.Fatal(err)
}
pubKey := prvKey.PublicKey
plain := "Golang 加密测试!!"
cipher, err := ECCEncrypt(plain, &pubKey)
if err != nil {
log.Fatal(err)
}
plain, err = ECCDecrypt(cipher, prvKey)
if err != nil {
log.Fatal(err)
}
fmt.Printf("明文:%s\n", plain)
}
3 哈希算法
1 简介
哈希算法输入可以是任意长度,但其输出是固定长度,其根据输入很难计算输出,不可逆,两个不同的输入几乎不可能得到相同的输出
2 sha1
SHA(Secure Hash Algorithm)安全散列算法,是一系列密码散列函数,有多个不同安全等级的版本,SHA-1,SHA-224,SHA-256,SHA-384,SHA-512,其主要用途是防伪装,防窜扰,保证信息的合法性和完整性
1 填充。使得数据长度对512求余的结果为448
2 在信息摘要的后面附加64bit,表示原始信息摘要的长度
3 初始化h0到h4,每个h 都是32位
4 h0到h4历经80轮复杂的变换
5 把h0到h4拼接起来,构成160位,返回
3 md5
MD5(Message-Digest Algorithm 5)信息-摘要算法5,该算法流程和SHA-1大体类似
MD5的输出是128位,比SHA-1短了32位
MD5相对容易受到密码分析的攻击,运算速度比SHA-1快
4 哈希函数的应用
1 用于用户存储密码
2 用于文件上传/下载的完整性校验
3 MySQL 大字段的快速对比
5 代码
package main
import (
"crypto/md5"
"crypto/sha1"
"encoding/hex"
"fmt"
)
func Sha1(data string) string {
sha1 := sha1.New()
sha1.Write([]byte(data))
return hex.EncodeToString(sha1.Sum(nil)) //SHA1 HASH
}
func Md5(data string) string {
md5 := md5.New()
md5.Write([]byte(data))
return hex.EncodeToString(md5.Sum(nil)) //MD5 HASH
}
func main() {
data := "Golang 测试!!"
fmt.Printf("SHA-1: %s\n", Sha1(data))
fmt.Printf("MD5: %s\n", Md5(data))
}
4 数字签名
1 简介
签名的实现过程:
输入:原文、私钥
输出:签名值
1、将原文做HASH
2、将HASH用私钥加密,结果就是签名值
验证签名的实现过程:
输入:签名值、原文、公钥
输出:是否验证通过
1、将原文做HASH1
2、将签名值用公钥解密,取得HASH2
3、将第1步的HASH1与第2步的HASH2做比较,两个HASH一样就验证通过,否则不通过
2 代码
package main
import (
"crypto/md5"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"os"
)
/**
生成1024位的RSA私钥:
openssl genrsa -out data/rsa_private_key.pem 1024
根据私钥生成公钥:
openssl rsa -in data/rsa_private_key.pem -pubout -out data/rsa_public_key.pem
pem是一种标准格式,它通常包含页眉和页脚
*/
var (
publicKey []byte
privateKey []byte
)
func ReadFile(keyFile string) ([]byte, error) {
if f, err := os.Open(keyFile); err != nil {
return nil, err
} else {
content := make([]byte, 4096)
if n, err := f.Read(content); err != nil {
return nil, err
} else {
return content[:n], nil
}
}
}
func ReadRSAKey(publicKeyFile, privateKeyFile string) (err error) {
if publicKey, err = ReadFile(publicKeyFile); err != nil {
return err
}
if privateKey, err = ReadFile(privateKeyFile); err != nil {
return err
}
return
}
// RSA加密
func RsaEncrypt(origData []byte) ([]byte, error) {
//解密pem格式的公钥
block, _ := pem.Decode(publicKey)
if block == nil {
return nil, errors.New("public key error")
}
// 解析公钥
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) //目前的数字证书一般都是基于ITU(国际电信联盟)制定的X.509标准
if err != nil {
return nil, err
}
// 类型断言
pub := pubInterface.(*rsa.PublicKey)
//加密
return rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
}
// RSA解密
func RsaDecrypt(ciphertext []byte) ([]byte, error) {
//解密
block, _ := pem.Decode(privateKey)
if block == nil {
return nil, errors.New("private key error")
}
//解析PKCS1格式的私钥
privInf, err := x509.ParsePKCS1PrivateKey(block.Bytes)
// ParsePPKCS1PrivateKey
if err != nil {
return nil, err
}
// 解密
return rsa.DecryptPKCS1v15(rand.Reader, privInf, ciphertext)
}
func Md5(data string) string {
md5 := md5.New()
md5.Write([]byte(data))
return hex.EncodeToString(md5.Sum(nil)) //MD5 HASH
}
func main() {
ReadRSAKey("data/rsa_public_key.pem", "data/rsa_private_key.pem")
plain := "GOlang 测试!!!"
md5sum := Md5(plain)
fmt.Printf("md5sum:%s\n", md5sum)
cipher, err := RsaEncrypt([]byte(md5sum))
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("密文:%s\n", hex.EncodeToString(cipher))
// fmt.Printf("密文:%v\n", (cipher))
bPlain, err := RsaDecrypt(cipher)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("MD5SUM明文:%s\n", string(bPlain))
}
}
}