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 简介

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))
}
}
}