加密通信

   一般的HTTPS是基于SSL(Secure Sockets Layer)协议。SSL是网景公司开发的位于TCP与HTTP之间的透明安全协议,通过SSL,可以把HTTP包数据以非对称加密的形式往返于浏览器和站点之间,从而避免被第三方非法获取。


1.加密通信流程

    当用户在浏览器中输入一个以https开头的网址时,便开启了浏览器与被访问站点之间的加密通信。下面我们以一个用户访问https://123.sogou.com为例,给读者展现一下SSL/TLS的工作方式。

    1).在浏览器中输入HTTPS协议的网址,如下图所示。


    2)服务器向浏览器返回证书,浏览器检查该证书的合法性,如下图所示:

加密通信_第1张图片

   3).验证合法性,如下图所示:

加密通信_第2张图片

   4).浏览器使用证书中的公钥加密一个随机对称密钥,并将加密后的密钥和使用密钥加密后的请求URL一起发送到服务器。

   5).服务器用私钥解密随机对称密钥,并用获取的密钥解密加密的请求URL。

   6).服务器把用户请求的网页用密钥加密,并返回给用户。

   7).用户浏览器用密钥解密服务器发来的网页数据,并将其显示出来。

   SSL协议由两层组成,上层协议包括SSL握手协议、更改密码规格协议、警报协议、下层协议包括SSL记录协议。

   SSL握手协议建立在SSL记录协议之上,在实际的数据传输开始前,用于客户与服务器之间进行“握手”。“握手”是一个协商过程。这个协议使得客户和服务器能够互相鉴别身份,协商加密算法。在任何数据传输之前,必须先进行“握手”。

   在“握手”完成之后,才能进行SSL记录协议,它的主要功能是为高层协议提供数据封装、压缩、添加MAC、加密等支持。


2.支持HTTPS的Web服务器

package main

import (
	"fmt"
	"net/http"
)

const SERVER_PORT = 8080
const SERVER_DOMAIN = "localhost"
const RESPONSE_TEMPLATE = "hello"

func rootHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	w.Header().Set("Content-Length", fmt.Sprint(len(RESPONSE_TEMPLATE)))
	w.Write([]byte(RESPONSE_TEMPLATE))
}
func main() {
	http.HandleFunc(fmt.Sprintf("%s:%d/", SERVER_DOMAIN, SERVER_PORT), rootHandler)
	http.ListenAndServeTLS(fmt.Sprintf(":%d", SERVER_PORT), "rui.crt", "rui.key", nil)
}
    可以看到,我们使用了http.ListenAndServerTLS()这个方法,这表明它是执行在TLS层上 的HTTP协议。如果我们并不需要支持HTTPS,只需要把该方法替换为http.ListenAndServeTLS (fmt.Sprintf(":%d", SERVER_PORT), nil)即可。


package main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"time"
)

const SERVER_PORT = 8080
const SERVER_DOMAIN = "localhost"
const RESPONSE_TEMPLATE = "hello"

func rootHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	w.Header().Set("Content-Length", fmt.Sprint(len(RESPONSE_TEMPLATE)))
	w.Write([]byte(RESPONSE_TEMPLATE))
}

func YourListenAndServeTLS(addr string, certFile string, keyFile string, handler http.Handler) error {
	config := &tls.Config{
		Rand:       rand.Reader,
		Time:       time.Now,
		NextProtos: []string{"http/1.1"},
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = YourLoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return err
	}
	conn, err := net.Listen("tcp", addr)
	if err != nil {
		return errs
	}
	tlsListener := tls.NewListener(conn, config)
	return http.Serve(tlsListener, handler)
}

func YourLoadX509KeyPair(certFile string, keyFile string) (cert tls.Certificate, err error) {
	certPEMBlock, err := ioutil.ReadFile(certFile)
	if err != nil {
		return
	}
	certDERBlock, restPEMBlock := pem.Decode(certPEMBlock)
	if certDERBlock == nil {
		err = errors.New("crypto/tls: failed to parse certificate PEM data")
		return
	}
	certDERBlockChain, _ := pem.Decode(restPEMBlock)
	if certDERBlockChain == nil {
		cert.Certificate = [][]byte{certDERBlock.Bytes}
	} else {
		cert.Certificate = [][]byte{certDERBlock.Bytes,
			certDERBlockChain.Bytes}
	}
	keyPEMBlock, err := ioutil.ReadFile(keyFile)
	if err != nil {
		return
	}
	keyDERBlock, _ := pem.Decode(keyPEMBlock)
	if keyDERBlock == nil {
		err = errors.New("crypto/tls: failed to parse key PEM data")
		return
	}
	key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes)
	if err != nil {
		err = errors.New("crypto/tls: failed to parse key")
		return
	}
	cert.PrivateKey = key
	x509Cert, err := x509.ParseCertificate(certDERBlock.Bytes)
	if err != nil {
		return
	}
	if x509Cert.PublicKeyAlgorithm != x509.RSA ||
		x509Cert.PublicKey.(*rsa.PublicKey).N.Cmp(key.PublicKey.N) != 0 {
		err = errors.New("crypto/tls: private key does not match public key")
		return
	}
	return
}

func main() {
	http.HandleFunc(fmt.Sprintf("%s:%d/", SERVER_DOMAIN, SERVER_PORT), rootHandler)
	YourListenAndServeTLS(fmt.Sprintf(":%d", SERVER_PORT), "rui.crt", "rui.key", nil)
}

rand,伪随机函数发生器,用于产生基于时间和CPU时钟的伪随机数; rsa,非对称加密算法, rsa是三个发明者名字的首字母拼接而成; tls,我们在上面已介绍过,它是传输层安全协议; x509,一种常用的数字证书格式;pem,在非对称加密体系下,一般用于存放公钥和私钥的文件。


3.支持HTTPS的文件服务器

package main

import (
	"net/http"
)

func main() {
	h := http.FileServer(http.Dir("."))
	http.ListenAndServeTLS(":8001", "rui.crt", "rui.key", h)
}


你可能感兴趣的:(安全编程)