对称加密

  加密过程的每一步都是可逆的。加密和解密用的是同一组密钥。异或是最简单的对称加密算法。

1
2
3
4
5
6
7
8
9
10
//XOR 异或运算,要求plain和key的长度相同
func XOR(plain string, key []byte) string {
bPlain := []byte(plain)
bCipher := make([]byte, len(key))
for i, k := range key {
bCipher[i] = k ^ bPlain[i]
}
cipher := string(bCipher)
return cipher
}

  DES(Data Encryption Standard)数据加密标准,是目前最为流行的加密算法之一。对原始数据(明文)进行分组,每组64位,最后一组不足64位时按一定规则填充。每一组上单独施加DES算法。
DES子密钥生成
  初始密钥64位,实际有效位56位,每隔7位有一个校验位。根据初始密钥生成16个48位的子密钥。

  N取值从1到16,N和x有固定的映射表。

DES加密过程

  L1, R1 = f(L0, R0, K1),依此循环,得到L16和R16。
  S盒替换。输入48位,输出32位。各分为8组,输入每组6位,输出每组4位。分别在每组上施加S盒替换,一共8个S盒。

DES加密过程

  分组模式。CBC(Cipher Block Chaining )密文分组链接模式,将当前明文分组与前一个密文分组进行异或运算,然后再进行加密。其他分组模式还有ECB, CTR, CFR, OFB。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func DesEncryptCBC(text string, key []byte) (string, error) {
src := []byte(text)
block, err := des.NewCipher(key) //用des创建一个加密器cipher
if err != nil {
return "", err
}
blockSize := block.BlockSize() //分组的大小,blockSize=8
src = common.ZeroPadding(src, blockSize) //填充

out := make([]byte, len(src)) //密文和明文的长度一致
encrypter := cipher.NewCBCEncrypter(block, key) //CBC分组模式加密
encrypter.CryptBlocks(out, src)
return hex.EncodeToString(out), nil
}

func DesDecryptCBC(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
}

out := make([]byte, len(src)) //密文和明文的长度一致
encrypter := cipher.NewCBCDecrypter(block, key) //CBC分组模式解密
encrypter.CryptBlocks(out, src)
out = common.ZeroUnPadding(out) //反填充
return string(out), nil
}

  AES(Advanced Encryption Standard)高级加密标准,旨在取代DES。

非对称加密

  • 使用公钥加密,使用私钥解密。
  • 公钥和私钥不同。
  • 公钥可以公布给所有人。
  • 私钥只有自己保存。
  • 相比于对称加密,运算速度非常慢。

  对称加密和非对称加密结合使用的案例。小明要给小红传输机密文件,他俩先交换各自的公钥,然后:

  1. 小明生成一个随机的AES口令,然后用小红的公钥通过RSA加密这个口令,并发给小红。
  2. 小红用自己的RSA私钥解密得到AES口令。
  3. 双方使用这个共享的AES口令用AES加密通信。

  RSA是三个发明人名字的缩写:Ron Rivest,Adi Shamir,Leonard Adleman。密钥越长,越难破解。 目前768位的密钥还无法破解(至少没人公开宣布)。因此可以认为1024位的RSA密钥基本安全,2048位的密钥极其安全。RSA的算法原理主要用到了数论。
RSA加密过程

  1. 随机选择两个不相等的质数p和q。p=61, q=53
  2. 计算p和q的乘积n。n=3233
  3. 计算n的欧拉函数φ(n) = (p-1)(q-1)。 φ(n) =3120
  4. 随机选择一个整数e,使得1< e < φ(n),且e与φ(n) 互质。e=17
  5. 计算e对于φ(n)的模反元素d,即求解e*d+ φ(n)*y=1。d=2753, y=-15
  6. 将n和e封装成公钥,n和d封装成私钥。公钥=(3233,17),公钥=(3233,2753)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 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格式的私钥
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
// 解密
return rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)
}

  ECC(Elliptic Curve Cryptography)椭圆曲线加密算法,相比RSA,ECC可以使用更短的密钥,来实现与RSA相当或更高的安全。定义了椭圆曲线上的加法和二倍运算。椭圆曲线依赖的数学难题是:k为正整数,P是椭圆曲线上的点(称为基点), k*P=Q , 已知Q和P,很难计算出k。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
}
}

哈希算法

哈希函数的基本特征

  1. 输入可以是任意长度。
  2. 输出是固定长度。
  3. 根据输入很容易计算出输出。
  4. 根据输出很难计算出输入(几乎不可能)。
  5. 两个不同的输入几乎不可能得到相同的输出。

  SHA(Secure Hash Algorithm) 安全散列算法,是一系列密码散列函数,有多个不同安全等级的版本:SHA-1,SHA-224,SHA-256,SHA-384,SHA-512。其作用是防伪装,防窜扰,保证信息的合法性和完整性。
sha-1算法大致过程

  1. 填充。使得数据长度对512求余的结果为448。
  2. 在信息摘要后面附加64bit,表示原始信息摘要的长度。
  3. 初始化h0到h4,每个h都是32位。
  4. h0到h4历经80轮复杂的变换。
  5. 把h0到h4拼接起来,构成160位,返回。
1
2
3
4
5
func Sha1(data string) string {
sha1 := sha1.New()
sha1.Write([]byte(data))
return hex.EncodeToString(sha1.Sum(nil))
}

  MD5(Message-Digest Algorithm 5)信息-摘要算法5,算法流程跟SHA-1大体相似。MD5的输出是128位,比SHA-1短了32位。MD5相对易受密码分析的攻击,运算速度比SHA-1快。

1
2
3
4
5
func Md5(data string) string {
md5 := md5.New()
md5.Write([]byte(data))
return hex.EncodeToString(md5.Sum(nil))
}

哈希函数的应用场景

  • 用户密码的存储。
  • 文件上传/下载完整性校验。
  • mysql大字段的快速对比。
    数字签名

  比特币中验证交易记录的真实性用的就是数字签名。先hash再用私钥加密的原因是:非对称加密计算量比较大,先hash可以把原始数据转一条很短的信息,提高计算效率。

案例

对称加密AES

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package main

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

func ZeroPadding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{0}, padding) //用0填充
return append(ciphertext, padtext...)
}

// ZeroUnPadding 这种方法不严谨,末尾的0不一定全是padding出来的
func ZeroUnPadding(origData []byte) []byte {
return bytes.TrimFunc(origData,
func(r rune) bool {
return r == rune(0) //截掉尾部连续的0
})
}

func AesEncrypt(text string, key []byte) (string, error) {
blockSize := aes.BlockSize //AES的分组大小为16
src := []byte(text)
src = ZeroPadding(src, blockSize) //填充
encrypted := make([]byte, len(src))
block, err := aes.NewCipher(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) //转为[]byte
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("ir489u58ir489u54") //key必须是长度为16的byte数组
plain := "因为我们没有什么不同"
cipher, err := AesEncrypt(plain, key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("密文:%s\n", cipher)

plain, err = AesDecrypt(cipher, key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("明文:%s\n", plain)
}

运行结果:

密文:854deaf62920930fa266eff575fb8fdd8d1473a3ebe345a3ebf87fd4232c1a2f
明文:因为我们没有什么不同

对称加密DES

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package main

import (
"bytes"
"crypto/cipher"
"crypto/des"
"encoding/hex"
"fmt"
"log"
)

func ZeroPadding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{0}, padding) //用0填充
return append(ciphertext, padtext...)
}

// ZeroUnPadding 这种方法不严谨,末尾的0不一定全是padding出来的
func ZeroUnPadding(origData []byte) []byte {
return bytes.TrimFunc(origData,
func(r rune) bool {
return r == rune(0) //截掉尾部连续的0
})
}

// DesEncrypt DES加密
// 密钥必须是64位,所以key必须是长度为8的byte数组
func DesEncrypt(text string, key []byte) (string, error) {
src := []byte(text)
block, err := des.NewCipher(key) //用des创建一个加密器cipher
if err != nil {
return "", err
}
blockSize := block.BlockSize() //分组的大小,blockSize=8
src = ZeroPadding(src, blockSize) //填充

out := make([]byte, len(src)) //密文和明文的长度一致
dst := out
for len(src) > 0 {
//分组加密
block.Encrypt(dst, src[:blockSize]) //对src进行加密,加密结果放到dst里
//移到下一组
src = src[blockSize:]
dst = dst[blockSize:]
}
return hex.EncodeToString(out), nil
}

// 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 DesEncryptCBC(text string, key []byte) (string, error) {
src := []byte(text)
block, err := des.NewCipher(key) //用des创建一个加密器cipher
if err != nil {
return "", err
}
blockSize := block.BlockSize() //分组的大小,blockSize=8
src = ZeroPadding(src, blockSize) //填充

out := make([]byte, len(src)) //密文和明文的长度一致
encrypter := cipher.NewCBCEncrypter(block, key) //CBC分组模式加密
encrypter.CryptBlocks(out, src)
return hex.EncodeToString(out), nil
}

func DesDecryptCBC(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
}

out := make([]byte, len(src)) //密文和明文的长度一致
encrypter := cipher.NewCBCDecrypter(block, key) //CBC分组模式解密
encrypter.CryptBlocks(out, src)
out = ZeroUnPadding(out) //反填充
return string(out), nil
}

func main() {

key := []byte("ir489u58") //key必须是长度为8的byte数组
plain := "因为我们没有什么不同"
cipher, err := DesEncrypt(plain, key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("密文:%s\n", cipher)

plain, err = DesDecrypt(cipher, key)
if err != nil {
log.Fatal(err)
}
fmt.Printf("明文:%s\n", plain)
fmt.Println("-------------------------------------")

cipher, _ = DesEncryptCBC(plain, key)
fmt.Printf("密文:%s\n", cipher)
plain, err = DesDecryptCBC(cipher, key)
fmt.Printf("明文:%s\n", plain)
}

运行结果:

密文:5dc1bc67723d97493e27e5f4993c5a8f743179e15671e34a63067838ce25311f
明文:因为我们没有什么不同
-------------------------------------
密文:3323587cb8e1c03f524cd87fbb0c2585fc71c713f5416cc9ae1ee3ca1350b28d
明文:因为我们没有什么不同

非对称加密ECC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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 := "因为我们没有什么不同"
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)
}

非对称加密RSA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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格式的私钥
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
// 解密
return rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)
}
func main() {
ReadRSAKey("D:\\rsa_public_key.pem", "D:\\rsa_private_key.pem")

plain := "因为我们没有什么不同"
cipher, _ := RsaEncrypt([]byte(plain))
fmt.Printf("密文:%s\n", hex.EncodeToString(cipher))
bPlain, _ := RsaDecrypt(cipher)
fmt.Printf("明文:%s\n", string(bPlain))
}

运行结果:

密文:03eb4e1a6b1444d5996d06e106f126ccb7803a3f3fb4d6ba7751734e410136cc442603b6adcdfaeef2bd7309931fc9f05a404c14ae31fffee7e48808e8b112d630568bbc39ccd5d799f0dfc6e021592c01ed8fac9b618030491813620fe69c891946b21e1dafcd4d8ca3897f6436a85f5548c8bf9e49ef2f13a70f8c0118efeb
明文:因为我们没有什么不同

数字签名验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package main

import (
"crypto"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
)

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

// go标准库不支持私钥加密,但直接提供了签名函数
func DigitalSignature(trade string) []byte {
sha1 := sha1.New()
sha1.Write([]byte(trade))
digest := sha1.Sum([]byte(""))

privateKey, _ := ReadFile("D:\\rsa_private_key.pem")
block, _ := pem.Decode(privateKey)
priv, _ := x509.ParsePKCS1PrivateKey(block.Bytes)
//用私钥生成签名
signature, _ := rsa.SignPKCS1v15(nil, priv, crypto.Hash(0), digest)
return signature
}

// 验证数字签名
func VerifySignature(trade string, signature []byte) bool {
sha1 := sha1.New()
sha1.Write([]byte(trade))
digest := sha1.Sum([]byte(""))

publicKey, _ := ReadFile("D:\\rsa_public_key.pem")
block, _ := pem.Decode(publicKey)
pubInterface, _ := x509.ParsePKIXPublicKey(block.Bytes)
pub := pubInterface.(*rsa.PublicKey)

//用公钥验证签名
return rsa.VerifyPKCS1v15(pub, crypto.Hash(0), digest, signature) == nil
}

func main() {
trade := "zhangsan transfer 10 BTC to lisi"
signature := DigitalSignature(trade)
fmt.Println("验证数字签名", VerifySignature(trade, signature))
}


运行结果:

验证数字签名 true

哈希

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main

import (
"bufio"
"crypto/md5"
"crypto/sha1"
"encoding/hex"
"fmt"
"io/ioutil"
"log"
"os"
)

func Sha1(data []byte) string {
sha1 := sha1.New()
sha1.Write(data)
return hex.EncodeToString(sha1.Sum(nil))
}

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

func main() {
data := "因为我们没有什么不同"
fmt.Printf("SHA-1: %s\n", Sha1([]byte(data)))
fmt.Printf("MD5: %s\n", Md5([]byte(data)))

f, err := os.Open("go.sum")
if err != nil {
log.Fatal(err)
}
defer f.Close()
r := bufio.NewReader(f)
b, err2 := ioutil.ReadAll(r)
if err2 != nil {
log.Fatal(err2)
}
fmt.Printf("Sha1(b): %v\n", Sha1(b))
fmt.Printf("Md5(b): %v\n", Md5(b))
}

运行结果:

SHA-1: 0cfea402af137e3793a6c8a80152b5ab74ba380b
MD5: 78cfcd6021c4f04e7e709cb8148aa4dd
Sha1(b): 4c8180f36e736ae3158767fc2a9f959bb3c86448
Md5(b): c82232187701537f2dd16d5ca836be72