TLS原理及实现

概念

对称密钥

既可以加密也可以解密的密钥

非对称密钥

密钥分为公钥和私钥两部分,公钥用于加密和验证签名,私钥用于解密和签名

证书

可理解为身份证,证书由证书机构颁发(可信第三方),证书中包括证书所有者的相关属性信息,并且证书可由证书机构的公钥进行验证,杜绝身份伪造。

证书链

由根证书衍生而来,例如根证书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通信):

  1. Client验证Server证书合法性,Client对Server的可信验证是通过CA证书链完成。
  2. Client验证Server证书合法后,Client本地生成会话密钥(对称密钥)sk。
  3. Client使用Server的公钥对会话密钥加密后的数据m并发送给Server。
  4. Server收到加密后的数据m后,使用自己的私钥解密m并获得会话密钥sk。
  5. Server使用会话密钥sk对“连接建立好的消息”加密并返回给Client。
  6. Client使用会话密钥sk解密后知晓连接已建立。
  7. Client与Server通过会话密钥进行安全传输。

实现

角色功能

CA

具有自签名、颁发证书、校验证书的功能。

Server

具有由CA颁发的证书,以及分发证书、签名、建立会话密钥安全通信的功能。

Client

获取Server证书后向CA请求校验证书,生成会话密钥并与Server协商后建立安全传输通道。

流程

启动角色的流程依次是CA、Server、Client,如下:

  1. 启动具有自签名证书的CA。

  2. 启动Server,并创建证书签名请求CSR(certificate signature request)发送给CA。

  3. CA获取到CSR之后,进行校验并生成签名证书返回给Server。

  4. 启动Client,Client发起获取Server证书的请求,Server返回自己的证书。

  5. Client向CA校验Server证书的合法性,并返回校验结果。

  6. Client生成会话密钥,并使用Server证书里的公钥对会话密钥加密后并发送给Server。

  7. Server使用私钥解密,获得会话密钥,并告知Client可以开始安全通信。

  8. 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
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值