概念
对称密钥
既可以加密也可以解密的密钥
非对称密钥
密钥分为公钥和私钥两部分,公钥用于加密和验证签名,私钥用于解密和签名
证书
可理解为身份证,证书由证书机构颁发(可信第三方),证书中包括证书所有者的相关属性信息,并且证书可由证书机构的公钥进行验证,杜绝身份伪造。
证书链
由根证书衍生而来,例如根证书R给A颁发证书,A给B颁发证书,B给C颁发证书,最终R->A->B->C形成一条证书链。证书链确保了次级证书的合法性,并允许验证,验证的过程是逆向的C->B->A->R,由于R肯定合法,所以能验证证书链中其它证书的合法性。
Client:
TLS的请求发起者
Server:
TLS请求响应者
原理
原理可概括为:Client通过验证Server身份并分享会话密钥,然后通过只有Server可Client知道的会话密钥进行通信。全流程如下(Client建立与Server的TLS通信):
- Client验证Server证书合法性,Client对Server的可信验证是通过CA证书链完成。
- Client验证Server证书合法后,Client本地生成会话密钥(对称密钥)sk。
- Client使用Server的公钥对会话密钥加密后的数据m并发送给Server。
- Server收到加密后的数据m后,使用自己的私钥解密m并获得会话密钥sk。
- Server使用会话密钥sk对“连接建立好的消息”加密并返回给Client。
- Client使用会话密钥sk解密后知晓连接已建立。
- Client与Server通过会话密钥进行安全传输。
实现
角色功能
CA
具有自签名、颁发证书、校验证书的功能。
Server
具有由CA颁发的证书,以及分发证书、签名、建立会话密钥安全通信的功能。
Client
获取Server证书后向CA请求校验证书,生成会话密钥并与Server协商后建立安全传输通道。
流程
启动角色的流程依次是CA、Server、Client,如下:
-
启动具有自签名证书的CA。
-
启动Server,并创建证书签名请求CSR(certificate signature request)发送给CA。
-
CA获取到CSR之后,进行校验并生成签名证书返回给Server。
-
启动Client,Client发起获取Server证书的请求,Server返回自己的证书。
-
Client向CA校验Server证书的合法性,并返回校验结果。
-
Client生成会话密钥,并使用Server证书里的公钥对会话密钥加密后并发送给Server。
-
Server使用私钥解密,获得会话密钥,并告知Client可以开始安全通信。
-
Client与Server开始使用会话密钥进行对数据加密后的通信。
时序图
关键代码
完整代码可点此跳转
CA
func (ca *CA) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
body, err := ioutil.ReadAll(req.Body)
if err != nil {
log.Fatal("Failed to read request message")
}
cmd := req.Header.Get("cmd")
switch cmd {
case "issue":
log.Printf("接收到来自 %s 证书签名请求...", req.RemoteAddr)
csr, err := x509.ParseCertificateRequest(body)
if err != nil {
log.Fatal("Failed to parse csr")
}
cert, err := ca.IssueCertificate(csr)
if err != nil {
log.Println("签名失败...")
}
log.Printf("签名成功,并返回给 %s", req.RemoteAddr)
resp.Header().Set("cmd", "issue-resp")
resp.Write(cert)
case "check":
log.Printf("接收到来自 %s 的证书校验请求...", req.RemoteAddr)
cert, err := x509.ParseCertificate(body)
if err != nil {
log.Fatal("Failed to parse child's certificate")
}
var result string = "success"
err = cert.CheckSignatureFrom(ca.cert)
if err != nil {
result = "failed"
}
log.Printf("签名校验结果: %s", result)
resp.Header().Set("cmd", "check-resp")
resp.Write([]byte(result))
default:
resp.Header().Set("cmd", "error")
resp.Write([]byte("Cmd is invalid"))
}
}
// IssueCertificate 颁发证书
func (ca *CA) IssueCertificate(csr *x509.CertificateRequest) ([]byte, error) {
// 校验证书中的国家代码必须是 CN, 实现校验过程
if csr.Subject.Country[0] != "CN" {
return nil, errors.New("The country must be CN")
}
// 根据证书请求csr组建证书格式
template := &x509.Certificate{
Subject: csr.Subject,
PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
PublicKey: csr.PublicKey,
SerialNumber: big.NewInt(time.Now().UnixNano()),
SignatureAlgorithm: x509.SHA256WithRSA,
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Hour * 24 * 30), // one month
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
}
// ca 使用自己的私钥与证书对证书进行签名
clientCert, _ := x509.CreateCertificate(rand.Reader, template, ca.cert, csr.PublicKey, ca.privKey)
return clientCert, nil
}
// CheckSignature 检查证书的签名是否由该CA签署
func (ca *CA) CheckSignature(cert *x509.Certificate) bool {
return cert.CheckSignatureFrom(ca.cert) == nil
}
Server
func (s *Server) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
body, err := ioutil.ReadAll(req.Body)
if err != nil {
log.Fatal("Failed to read request message")
}
cmd := req.Header.Get("cmd")
switch cmd {
case "get-cert":
log.Printf("收到来自 %s 的获取证书请求", req.RemoteAddr)
resp.Write(s.certAsn1Data)
case "sign":
log.Printf("收到来自 %s 的签名请求", req.RemoteAddr)
digest := sha256.Sum256(body)
signData, _ := s.privKey.Sign(rand.Reader, digest[:], crypto.SHA256)
resp.Write(signData)
default:
log.Printf("收到来自 %s 的非法命令:%s", req.RemoteAddr, cmd)
}
}
// GetCertificateFromCA 获取由CA颁发的证书。向CA发起证书请求,CA验证后颁发证书
func (s *Server) GetCertificateFromCA() error {
csrTemplate := &x509.CertificateRequest{
Signature: []byte("zut"),
Subject: pkix.Name{
Country: []string{"CN"},
Organization: []string{"zut"},
OrganizationalUnit: []string{"unit"},
CommonName: "zut",
},
SignatureAlgorithm: x509.SHA256WithRSA,
}
// 私钥用于对CSR签名,以及将私钥对应的公钥写入到CSR中
csrAsnaData, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, s.privKey)
if err != nil {
return errors.New(fmt.Sprintf("Failed to create certificate request: %v", err))
}
log.Println("创建证书签名请求 CSR 成功...")
// 向ca发起http请求
req, _ := http.NewRequest("POST", "http://0.0.0.0:20000", bytes.NewBuffer(csrAsnaData))
req.Header.Set("cmd", "issue")
resp, _ := s.httpClient.Do(req)
// 解析asn1编码的证书
certAsn1Data, _ := ioutil.ReadAll(resp.Body)
cert, err := x509.ParseCertificate(certAsn1Data)
if err != nil {
return errors.New(fmt.Sprintf("Failed to parse certificate: %v", err))
}
log.Println("证书请求由CA签名成功...")
s.certAsn1Data = certAsn1Data
s.cert = cert
tools.WriteToFile("server-cert.pem", "CERTIFICATE", certAsn1Data)
tools.WriteToJsonFile("server-cert.json", cert)
log.Println("证书文件写入 server-cert.pem server-cert.json...")
return nil
}
Client
// GetServerCertificate 获取服务端的证书
func (c *Client) GetServerCertificate() error {
// 发起http请求
req, _ := http.NewRequest("GET", "http://0.0.0.0:20001", bytes.NewBuffer([]byte("")))
req.Header.Set("cmd", "get-cert")
resp, _ := c.httpClient.Do(req)
// 获取到返回的数据
certAsn1Data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
c.srvCertAsn1Data = certAsn1Data
c.srvCert, _ = x509.ParseCertificate(certAsn1Data)
tools.WriteToFile("server-cert.pem", "SERVER CERTIFICATE", certAsn1Data)
return nil
}
// CheckServerCertificateFromCA 向CA验证获取到的服务端证书
func (c *Client) CheckServerCertificateFromCA() bool {
// 发起http请求
req, _ := http.NewRequest("POST", "http://0.0.0.0:20000", bytes.NewBuffer(c.srvCertAsn1Data))
req.Header.Set("cmd", "check")
resp, _ := c.httpClient.Do(req)
// 获取到返回的数据
ok, _ := ioutil.ReadAll(resp.Body)
return string(ok) == "success"
}
// SignByServer 请求服务端对msg进行签名
func (c *Client) SignByServer(msg []byte) (signature []byte) {
req, _ := http.NewRequest("POST", "http://0.0.0.0:20001", bytes.NewBuffer(msg))
req.Header.Set("cmd", "sign")
resp, _ := c.httpClient.Do(req)
signature, _ = ioutil.ReadAll(resp.Body)
return
}
func (c *Client) VerifyServerSignature(signed, signature []byte) bool {
return c.srvCert.CheckSignature(x509.SHA256WithRSA, signed, signature) == nil
}