PKI的简单理解和实现

Public Key Infrastructure(PKI),中文叫做公开密钥基础设施,也就是利用公开密钥机制建立起来的基础设施。

PKI的简单理解

PKI的核心是身份证明书的发行

PKI的主要目的是用来发行“身份证明书”,在互联网相互通信的时候,如果能相互确认身份证明书,那么我们就知道自己是在跟对的人通信。

网络世界中,我们需要一个信得过的发证机关来发行身份证明书,同时自己要好好保管自己的身份证明书,就像派出所给你发了公民身份证,自己要好好保管一样。

PKI的世界里,这个身份证明书,被叫做“证明书”。发行“证明书”的机关叫做“认证机关”。还有一个就是统一管理证明书的证书“档案库”。这三个东西加起来,就是PKI的主要构成要素。

构成PKI的要素只有三个

构成PKI的主要要素就是下面三个概念

  1. 证明书 Cert
  2. 认证机关 CA
  3. 证书库

说到底,PKI指的是证明书的制作和分发的一种机制。在这个机制的保障前提下,进行可信赖的网络通信。即安全的网路通信保障机制。

  • 证明书被存放在硬盘里。证明书的文件构造是一种叫做 X.509 的协议规定的。

  • 认证机关也其实就是一个网络应用程序。

  • 证书库只是一个数字证书(公钥签名)的集合,保存用户和数字证书的内容和对应关系。

比如说,你要和一个自称比尔的男人通信。这个自称比尔的男人,会在通信的最开始,通过网络将证明书发给你,那么通过这个证明书,就证实了他就是比尔。

然后,你用这个“证明书中的公钥”,将你要发送给比尔的内容进行加密,然后发送给比尔。

用“证明书中的公钥”加密过的内容,只能用比尔自己才有的另一个“私钥”才能解密。这样的话,如果你发送给比尔的内容被他人窃取的话,他人也无法解密。

但如何保证比尔的证明书是正确的呢?

这个认证机关就至关重要了,认证机关的可信度,直接与证书的可信度挂钩,也就是与整个PKI机制的可信度息息相关。

认证机关对比尔的公钥进行了数字签名,并生成了证书。比尔拿着认证机关给的证书,在一开始发送身份证明书给我时,就夹带了这个认证证书,而我拿到比尔的身份证明书后,再得到认证机构的公钥。用认证机构的公钥的验证比尔的认证证书是否是CA颁发的。只要我相信CA不会认错人,那我就相信CA的结果,即这个信息确实是比尔发给我的。

假如我想和比尔通讯,流程如下:

  • 比尔找CA验明正身,请求获得一个证书
  • CA验明以后,CA使用自己的私钥对比尔的公钥进行签名,生成比尔的证书
  • 比尔拿着证书向我证明,自己就是比尔
  • 我选择向CA询问他的公钥,用来对证书进行验证,得到比尔的公钥,所以这是比尔的证书
  • 我在用比尔的公钥对消息进行加密后发给比尔
  • 比尔拿到后用自己的私钥解密即可

PKI的简单实现

有了以上的理论基础后,我选择使用GO语言对以上的流程做简单的实现。

使用GO的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
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
129
130
131
132
133
134
135
136
137
138
package main

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

const bits = 2048
const FilePath = "./pki/certs/"

// GenerateRSAKey generate a pair of public key and private key
func GenerateRSAKey(username string) {
// 保存私钥
//GenerateKey函数使用随机数据生成器random生成一对具有指定字位数的RSA密钥
//Reader是一个全局、共享的密码用强随机数生成器
privateKey,_ := rsa.GenerateKey(rand.Reader,bits)
//通过x509标准将得到的ras私钥序列化为ASN.1 的 DER编码字符串
X509PrivateKey := x509.MarshalPKCS1PrivateKey(privateKey)
//使用pem格式对x509输出的内容进行编码 并创建文件保存私钥
privateKeyFile,_ := os.Create(FilePath + username + "_privateKey.pem")
defer privateKeyFile.Close()
//构建一个pem.Block结构体对象
privateBlock := pem.Block{Type: "RSA Private Key",Bytes: X509PrivateKey}
//将数据保存到文件
pem.Encode(privateKeyFile,&privateBlock)

// 保存公钥,基本同上
publicKey := privateKey.PublicKey
//X509对公钥编码
X509PublicKey, _ := x509.MarshalPKIXPublicKey(&publicKey)
//pem格式编码, 创建用于保存公钥的文件
publicKeyFile, _ := os.Create(FilePath + username + "_publicKey.pem")
defer publicKeyFile.Close()
//创建一个pem.Block结构体对象
publicBlock := pem.Block{Type: "RSA Public Key", Bytes: X509PublicKey}
//保存到文件
pem.Encode(publicKeyFile, &publicBlock)
}


// Get_Sign sign by private key
func Get_Sign(msg []byte,path string)[]byte{
//取得私钥
privateKey:=GetRSAPrivateKey(path)
//计算散列值
hash := sha256.New()
hash.Write(msg)
bytes := hash.Sum(nil)
//SignPKCS1v15使用RSA PKCS#1 v1.5规定的RSASSA-PKCS1-V1_5-SIGN签名方案计算签名
sign, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, bytes)
if err!=nil{
panic(sign)
}
return sign
}


// Verify_Sign verify by public key
func Verify_Sign(msg []byte,sign []byte,path string) bool{
//取得公钥
publicKey := GetRSAPublicKey(path)
//计算消息散列值
hash := sha256.New()
hash.Write(msg)
bytes := hash.Sum(nil)
//验证数字签名
err := rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, bytes, sign)
return err==nil
}


// RSA_Encrypt encryption by public key
func RSA_Encrypt(plainText []byte, path string) []byte {
// 获取公钥
publicKey := GetRSAPublicKey(path)
//对明文进行加密
cipherText, _ := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText)
//返回密文
return cipherText
}

// RSA_Decrypt decryption by private key
func RSA_Decrypt(cipherText []byte,path string) []byte{
// 获取私钥
privateKey := GetRSAPrivateKey(path)
//对密文进行解密
plainText,_ := rsa.DecryptPKCS1v15(rand.Reader,privateKey,cipherText)
//返回明文
return plainText
}



//读取RSA私钥
func GetRSAPrivateKey(path string)*rsa.PrivateKey{
//读取文件内容
file, err := os.Open(path)
if err!=nil{
panic(err)
}
defer file.Close()
info, _ := file.Stat()
buf:=make([]byte,info.Size())
file.Read(buf)
//pem解码
block, _ := pem.Decode(buf)
//X509解码
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
return privateKey
}


//读取RSA公钥
func GetRSAPublicKey(path string) *rsa.PublicKey{
//读取公钥内容
file, err := os.Open(path)
if err!=nil{
panic(err)
}
defer file.Close()
info, _ := file.Stat()
buf:=make([]byte,info.Size())
file.Read(buf)
//pem解码
block, _ := pem.Decode(buf)
//x509解码
publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err!=nil{
panic(err)
}
publicKey := publicKeyInterface.(*rsa.PublicKey)
return publicKey
}

模拟B向A发送消息的流程,中间省略一些消息传递的流程,重点关注身份验证和信息安全的部分。

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
package main

import (
"fmt"
"os"
)

func main() {
// 创建A,B,CA
GenerateRSAKey("A")
GenerateRSAKey("B")
GenerateRSAKey("CA")

// A 申请证书
A_pub_file, _ := os.Open(FilePath + "A_publicKey.pem")
defer A_pub_file.Close()
info, _ := A_pub_file.Stat()
A_pub := make([]byte,info.Size())
A_pub_file.Read(A_pub)

// CA 对 A 的公钥进行签名 ,得到A的证书
A_sign := Get_Sign(A_pub,FilePath + "CA_privateKey.pem")

// A 发送证书和publicKey给 B 此过程省略

// B 校验 A 的证书合法性 ,省略找CA 拿public Key 的过程
f := Verify_Sign(A_pub,A_sign,FilePath + "CA_publicKey.pem")
if f{
fmt.Println("verify is success!")
}
// 验证通过以后 B用A的pub Key把消息加密发送给 A , 发送过程省略
de_msg := RSA_Encrypt([]byte("hello world."),FilePath + "A_publicKey.pem")

// A 拿到消息以后 用自己的私钥解密
msg := RSA_Decrypt(de_msg,FilePath + "A_privateKey.pem")
fmt.Println(string(msg))
}

输出结果:

1
2
verify is success!
hello world.

参考:

5分钟让你知道什么是PKI - 知乎 (zhihu.com)