Road to growth of rookie

Meaningful life is called life

0%

Golang 实现的非对称加密

对称加密作为最快速、最简单的加密方式, 所以被广泛的使用在很多加密协议的核心中. 但是对称加密也有一个致命的弱点, 那就是对称加密算法在加密和解密是需要使用到同一个 密钥; 当 AB 两个人在需要进行加密数据的传输时, 如果他们使用的是对称加密算法, 那么必须有一端将 密钥 发送给另外一端. 在不安全的网络传输中这一点就是相当危险! 密钥 一旦被窃或拦截, 那么之前所做的加密就相当于已经失效.

1976 年, 美国学者 DimeHenman 为了解决信息公开传送和秘钥管理问题, 提出一种新的密钥交换协议, 允许在不安全媒体上的通信双方交换数据, 安全的达成一致的密钥, 这种密钥交换协议就被称为 “**公开密钥密码学 (Public-key cryptography)**”. 相对于 对称加密算法 这种方法也被叫做 非对称加密算法.

非对称加密算法之所以可以规避对称加密的致命缺点, 就是因为非对称加密使用的是 一对 密钥, 而不是像对称加密一样使用 一个 密钥; 非对称加密的秘钥分为两个: 一个是 公钥 (public key); 另一个则是 私钥 (private key). 私钥只由安全的一方保管, 不能外泄; 公钥则可以发给任何请求它的人. 非对称加密算法在加密时可以使用两个密钥中的任何一个, 但是在解密时则需要另外一个密钥才能解密. 比如: 使用公钥加密就需要私钥才能解密、使用私钥加密就需要公钥才能解密.

RSA 非对称加密算法

非对称加密的主要算法有: RSAElgamal、背包算法、RabinD-HECC (椭圆曲线加密算法), 其中 RSA 是被最广泛使用的一种非对称加密算法. RSA 是 1977 年由 Ronald Linn RivestAdi ShamirLeonard Max Adleman 三个人一起提出, RSA 就是他们三个人的姓氏首字母拼在一起组成的

RSA 跟对称加密比较, 更加复杂, 且更加消耗资源. 但是相对来说它的灵活性也更高; 到目前为止, 世界上还没有任何可靠的能够攻击 RSA 算法的方式. 只要其密钥长度足够长, 用 RSA 加密的数据实际上是不能破解的

ASN.1

在电信和计算机网络领域, ASN.1 (Abstract Syntax Notation One) 是一套标准, 是描述数据的表示、编码、传输、解码的灵活的记法. 它提供了一套正式、无歧义和精确的规则以描述独立于特定计算机硬件的对象结构. 标准的 ASN.1 编码规则有: 基本编码规则 (Basic Encoding Rules) 简称 BER、规范编码规则 (Canonical Encoding Rules) 简称 CER、唯一编码规则 (Distinguished Encoding Rules) 简称 DER、压缩编码规则 (Packed Encoding Rules) 简称 PERXML 编码规则 (XML Encoding Rules) 简称 XER. 参考资料

X.509 数字证书标准

X.509 是密码学里公钥证书的格式标准. X.509 证书已应用在包括 TLS/SSL 在内的众多网络协议里, 同时它也用在很多非在线应用场景里, 比如电子签名服务. X.509 证书里含有公钥、身份信息 (比如网络主机名,组织的名称或个体名称等) 和签名信息 (可以是证书签发机构CA的签名,也可以是自签名). 对于一份经由可信的证书签发机构签名或者可以通过其它方式验证的证书, 证书的拥有者就可以用证书及相应的私钥来创建安全的通信, 对文档进行数字签名. X.509 证书的结构是由 ASN.1 进行描述, 并使用 ASN.1 DER 编码规则编码

golang 实现 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
package main

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

func generatePrinAndPubKeys(size int) {
pri, err := rsa.GenerateKey(rand.Reader, size)
if err != nil {
panic(fmt.Sprintf("generate rsa keys fail: %s", err))
}

derTx := x509.MarshalPKCS1PrivateKey(pri)
block := pem.Block{Type: "RSA PRIVATE KEY", Bytes: derTx}
file, err := os.Create("private.pem")
if err != nil {
panic(fmt.Sprintf("create file fail: %s", err))
}
defer file.Close()

if err := pem.Encode(file, &block); err != nil {
panic(fmt.Sprintf("private key input file fail: %s", err))
}

derstream, err := x509.MarshalPKIXPublicKey(&pri.PublicKey)
if err != nil {
panic(fmt.Sprintf("marshal public key fail: %s", err))
}
block = pem.Block{Type: "RSA PUBLIC KEY", Bytes: derstream}
pubFile, err := os.Create("public.pem")
if err != nil {
panic(fmt.Sprintf("create file fail: %s", err))
}
defer pubFile.Close()
if err := pem.Encode(pubFile, &block); err != nil {
panic(fmt.Sprintf("private key input file fail: %s", err))
}
}

func main() {
generatePrinAndPubKeys(1024)
}
golang 实现 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
package main

import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"io/ioutil"
)

func publicKeyEncrypt(plainText []byte, keyFile string) []byte {
content, err := ioutil.ReadFile(keyFile)
if err != nil {
panic(fmt.Sprintf("read public key file fail: %s", err))
}
block, _ := pem.Decode(content)
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
panic(fmt.Sprintf("parse public key fail: %s", err))
}
pubKey := pubInterface.(*rsa.PublicKey)
cipherTxt, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, plainText)
if err != nil {
panic(fmt.Sprintf("encrypt plain fail: %s", err))
}

return cipherTxt
}

func privateKeyDecrypt(cipherTxt []byte, keyFile string) []byte {
content, err := ioutil.ReadFile(keyFile)
if err != nil {
panic(fmt.Sprintf("read private key file fail: %s", err))
}
block, _ := pem.Decode(content)
priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
panic(fmt.Sprintf("parse private key fail: %s", err))
}

plainTxt, err := rsa.DecryptPKCS1v15(rand.Reader, priKey, cipherTxt)
if err != nil {
panic(fmt.Sprintf("decrypt cipher fail: %s", err))
}

return plainTxt
}

func main() {
txt := "this is testing text"
cipher := publicKeyEncrypt([]byte(txt), "public.pem")
fmt.Printf("%v\n", base64.StdEncoding.EncodeToString(cipher))
fmt.Printf("%s\n", string(privateKeyDecrypt(cipher, "private.pem")))
}
golang 实现 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
package main

import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
)

func signByRsa(priFile string, plaintext []byte) []byte {
content, err := ioutil.ReadFile(priFile)
if err != nil {
panic(fmt.Sprintf("read private key file fail: %s", err))
}

derTxt, _ := pem.Decode(content)
priKey, err := x509.ParsePKCS1PrivateKey(derTxt.Bytes)
if err != nil {
panic(fmt.Sprintf("parse private key fail: %s", err))
}

h := sha256.New()
h.Write(plaintext)

hashed, err := rsa.SignPKCS1v15(rand.Reader, priKey, crypto.SHA256, h.Sum(nil))
if err != nil {
panic(fmt.Sprintf("sign plaintext fail: %s", err))
}

return hashed
}

func verifyByRsa(pubFile string, plaintext, sign []byte) bool {
content, err := ioutil.ReadFile(pubFile)
if err != nil {
panic(fmt.Sprintf("read public key file fail: %s", err))
}

derTxt, _ := pem.Decode(content)
pubInterface, err := x509.ParsePKIXPublicKey(derTxt.Bytes)
if err != nil {
panic(fmt.Sprintf("parse public key fail: %s", err))
}
pubKey := pubInterface.(*rsa.PublicKey)

h := sha256.New()
h.Write(plaintext)

if err := rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, h.Sum(nil), sign); err != nil {
return false
}

return true
}

func main() {
txt := []byte("this is testing text")
sign := signByRsa("private.pem", txt)
fmt.Printf("sign verify result: %v\n", verifyByRsa("public.pem", txt, sign))
}

golang 中我没有找到可以使用 私钥 加密, 公钥 解密的接口. 可能需要自己实现

椭圆曲线非对称加密算法

椭圆曲线密码学 (Elliptic Curve Cryptography) 简称 ECC, 这是 Golang 实现的另外一种非对称加密算法, 它是一种基于 椭圆曲线数学 的公开密钥加密算法. ECC 的主要优势在于, 在某些情况下它比其他算法 (比如 RSA) 使用更小的密钥提供相当的或更高级的安全

Golang 标准包里并没有实现 ECC 的加密和解密, 只实现了基于 ECC 的数字签名和校验, 但是 以太坊 go-ethereum 中实现了 ECC 的加密和解密, 有兴趣的话可以参考一下

Golang 实现生成 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
package main

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
)

func generatePriAndPubKeys(curve elliptic.Curve) {
priKey, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
panic(fmt.Sprintf("generate private key fail: %s", err))
}

derTxt, err := x509.MarshalECPrivateKey(priKey)
if err != nil {
panic(fmt.Sprintf("marshal private key fail: %s", err))
}

block := pem.Block{Type: "ECC PRIVATE KEY", Bytes: derTxt}
file, err := os.Create("ecc_private.pem")
if err != nil {
panic(fmt.Sprintf("create private key file fail: %s", err))
}
defer file.Close()

if err := pem.Encode(file, &block); err != nil {
panic(fmt.Sprintf("write private key fail: %s", err))
}

pubDerTxt, err := x509.MarshalPKIXPublicKey(&priKey.PublicKey)
if err != nil {
panic(fmt.Sprintf("marshal public key fail: %s", err))
}

pubBlock := pem.Block{Type: "ECC PRIVATE KEY", Bytes: pubDerTxt}
pubFile, err := os.Create("ecc_public.pem")
if err != nil {
panic(fmt.Sprintf("create private key file fail: %s", err))
}
defer pubFile.Close()

if err := pem.Encode(pubFile, &pubBlock); err != nil {
panic(fmt.Sprintf("write private key fail: %s", err))
}
}

func main() {
generatePriAndPubKeys(elliptic.P256())
}
golang 实现 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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package main

import (
"crypto/ecdsa"
"crypto/rand"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"math/big"
)

func signByEcc(priFile string, plaintext []byte) (rTxt, sTxt []byte) {
content, err := ioutil.ReadFile(priFile)
if err != nil {
panic(fmt.Sprintf("read private key file fail: %s", err))
}

block, _ := pem.Decode(content)
priKey, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
panic(fmt.Sprintf("parse private key fail: %s", err))
}

h := sha256.New()
h.Write(plaintext)

r, s, err := ecdsa.Sign(rand.Reader, priKey, h.Sum(nil))
if err != nil {
panic(fmt.Sprintf("sign fail: %s", err))
}

rTxt, _ = r.MarshalText()
sTxt, _ = s.MarshalText()

return rTxt, sTxt
}

func verifyByEcc(pubFile string, plaintext, rTxt, sTxt []byte) bool {
content, err := ioutil.ReadFile(pubFile)
if err != nil {
panic(fmt.Sprintf("read public key file fail: %s", err))
}

block, _ := pem.Decode(content)
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
panic(fmt.Sprintf("parse public key fail: %s", err))
}
pubKey := pubInterface.(*ecdsa.PublicKey)
r, s := new(big.Int), new(big.Int)
if err := r.UnmarshalText(rTxt); err != nil {
panic(fmt.Sprintf("unmarshal r fail: %s", err))
}
if err := s.UnmarshalText(sTxt); err != nil {
panic(fmt.Sprintf("unmarshal s fail: %s", err))
}

h := sha256.New()
h.Write(plaintext)

return ecdsa.Verify(pubKey, h.Sum(nil), r, s)
}

func main() {
txt := []byte("this is testing text")
r, s := signByEcc("ecc_private.pem", txt)
fmt.Printf("sign verify result: %v\n", verifyByEcc("ecc_public.pem", txt, r, s))
}

参考和引用