TLS安全通信原理

加密

对称加密

加密方和解密方使用同一把密钥

  • 优点:算法简单,加密解密容易, 效率高,执行快

  • 缺点:相对来说不算特别安全,只有一把钥匙,密文如果被拦截,且密钥也被劫持,那么,信息很容易被破译。 核心问题在于通信双方如何安全的传递密钥

非对称加密

非对称加密有两个钥匙,及公钥(Public Key)和私钥(Private Key)。公钥和私钥是成对的存在,如果对原文使用公钥加密,则只能使用对应的私钥才能解密;因为加密和解密使用的不是同一把密钥,所以这种算法称之为非对称加密算法。

非对称加密算法的密匙是通过一系列算法获取到的一长串随机数,通常随机数的长度越长,加密信息越安全。 通过私钥经过一系列算法是可以推导出公钥的 ,也就是说,公钥是基于私钥而存在的。但是无法通过公钥反向推倒出私钥,这个过程的单向的。

私钥加密的东西,公钥能解密;公钥加密的内容,私钥也能解密;

优点:安全, 即使密文被拦截、公钥被获取,但是无法获取到私钥,也就无法破译密文 。作为接收方,务必要保管好自己的密钥。

缺点:加密算法及其复杂,安全性依赖算法与密钥,而且 加密和解密效率很低

安全风险

  • 窃听风险: A与B通信内容即使被C拦截到了,C也不能破解其中的内容
  • 篡改风险: A与B通信内容即使被C拦截到了,C虽然不知道通信内容,但是C可以修改内容,A、B怎么知道内容被修改了?
  • 冒充风险: C冒充A与B通信,B怎么确认身份?

TLS

  • 数字签名
    • A先对内容做hash,把内容及hash值一同传给B,B接收到之后也对内容做hash,然后比对此hash值和A传递过来的hash值是否一致,如果不一致说明内容被修改了。
    • 如果C拦截到了A发送的内容及hash值,C先把内容修改了再重新计算hash值,发送给B怎么办?
    • A用加密算法对hash值加密得到sign,B接收到之后先对sign做解密得到hash,再计算内容的hash值做比对,这样中间人是无法伪造sign的
    • 数字签名解决了 篡改风险冒充风险
  • 数字证书
    • A与B通信,A先把公钥发送给B,B用公钥加密数据A用私钥解密数据
    • A把公钥发送给B时被C给拦截了,C把自己公钥给了B,B使用C给的公钥加密数据,数据可以被C使用自己的私钥解密,怎么办?
    • A先把公钥发送给B时,发送给B 公钥+A的身份信息 组成的数字证书,数据证书用 CA的私钥 签名,B拿到数字证书后用 CA的公钥 解密证书即可
    • CA的公钥 也即CA证书文件时内置在浏览器、操作系统中的,所以用户可以信任

tls解决方案

对称加密与非对称加密的组合,解决网络安全问题

https

TLS握手过程

  1. HTTPS 协议会先与服务器执行 TCP 握手,然后执行 TLS 握手,才能建立安全连接;
  2. 握手的目标是安全地交换对称密钥,需要三个随机数,第三个随机数“Pre-Master”必须加密传输,绝对不能让黑客破解;(TLS设计者不信任客户端或服务器伪随机数的可靠性,为了保证真正的“完全随机”“不可预测”,把三个不可靠的随机数混合起来,那么“随机”的程度就非常高了,足够让黑客难以猜测)
  3. “Hello”消息交换随机数,“Key Exchange”消息交换“Pre-Master”;
  4. “Change Cipher Spec”之前传输的都是明文,之后都是对称密钥加密的密文。
  5. 握手之后的对话使用”对话密钥”加密(对称加密),服务器的公钥和私钥只用于加密和解密”对话密钥”(非对称加密),无其他作用。

客户端如何校验服务端发送的证书是否合法

  1. 证书是否过期
  2. 服务器证书上的域名是否和服务器的实际域名相匹配
  3. 根据证书上写的CA签发机构,在浏览器内置的根证书里找到对应的公钥(如果找不到则报不受信任的CA机构颁发的证书),用此公钥解开数字签名,得到摘要(digest,证书内容的hash值),据此验证证书的合法性。
    //不校验证书 InsecureSkipVerify

双向认证

  • 服务端

在go中启动一个https 服务,可以调用http.server 对象的 ListenAndServeTLS(certFile, keyFile string)方法。 通过注释和实现代码可以知道,该方法的两个参数分别是证书和私钥。也就是server.crt和server.key。并且同时还从http.server对象中获取了一个TLSConfig。TLSConfig 中设置的是根证书ca.crt。

pool := x509.NewCertPool()
    caCertPath := "ca.crt"

    caCrt, err := ioutil.ReadFile(caCertPath)
    if err != nil {
        fmt.Println("ReadFile err:", err)
        return
    }
    pool.AppendCertsFromPEM(caCrt)
    
     //初始化一个server 实例。
    s := &http.Server{
        //设置宿主机的ip地址,并且端口号为8081
        Addr:    ":8081",
        Handler: &myhandler{},
        TLSConfig: &tls.Config{
            ClientCAs:  pool,
            ClientAuth: tls.RequireAndVerifyClientCert,
        },
    }

    err = s.ListenAndServeTLS("server.crt", "server.key")
  • 客户端

客户端访问服务端的时候,设置其Transport参数。在构建Transport参数的时候,设置根证书ca.crt和client.crt,client.key。

pool := x509.NewCertPool()
    caCertPath := "ca.crt"

    caCrt, err := ioutil.ReadFile(caCertPath)
    if err != nil {
        fmt.Println("ReadFile err:", err)
        return
    }
    pool.AppendCertsFromPEM(caCrt)

    cliCrt, err := tls.LoadX509KeyPair("client.crt", "client.key")
    if err != nil {
        fmt.Println("Loadx509keypair err:", err)
        return
    }

    tr := &http.Transport{
        TLSClientConfig: &tls.Config{
            RootCAs:      pool,
            Certificates: []tls.Certificate{cliCrt},
        },
    }
    client := &http.Client{Transport: tr}

参考资料