go https 笔记

理解为运行在SSL(Secure Sockets Layer)或TLS(Transport Layer Security)协议所构建的安全层之上的HTTP协议

SSL和TLS

SSL/TLS协议运行机制的概述

图解SSL/TLS协议

自建证书

在使用https 之前,需要自己建立一个证书, 以便测试使用。正式使用需要去权威的CA机构申请证书。

自建证书配置HTTPS服务器

使用Go实现TLS 服务器和客户端

这个时候需要用到openssl 但是低版本的是有漏洞的,需要升级一下。

openssl是一个开源程序的套件、这个套件有三个部分组成:

  1. libcryto,这是一个具有通用功能的加密库,里面实现了众多的加密库;
  2. libssl,这个是实现ssl机制的,它是用于实现TLS/SSL的功能;
  3. openssl,是个多功能命令行工具,它可以实现加密解密,甚至还可以当CA来用,可以让你创建证书、吊销证书。

mac 安装升级openssl

其他系统使用连接中的方式下载升级, 因为我用的mac, 升级过程碰到一些问题,所以这里记一下。

OSX 有一个系统完整性保护,所以没权限删除系统自带的openssl, 但是我们不需要删除,只需要做软连接就可以解决

  1. 确认环境变量 $PATH
echo $PATH //打印环境变量

/usr/local/bin:<....>:/usr/bin:/bin

设置/usr/local/bin/usr/bin 之前,这样保证我们设置到/usr/local/bin先起作用。

如果不是这样,就需要设置$PATH

  1. 修改 $PATH
echo $SHELL   //确认自己使用的shell

/bin/bash  //修改 ~/.bash_profile

或

/bin/zsh  //修改 ~/.zshrc
  1. homebrew 安装和软连接
brew install openssl

brew link openssl

//如果失败 加参数 --farce

brew link openssl --farce
  1. 新打开终端, 查看版本
openssl version -a

证书生成

服务器端的证书生成

互联网全站HTTPS的时代已经到来

//生成服务器端的私钥, 要使用2048位生成,1024位已经不安全了, 看上面的链接
openssl genrsa -out server.key 2048

//生成服务器端证书
openssl req -new -x509 -key server.key -out server.pem -days 365

客户端的证书生成
//生成客户端的私钥
openssl genrsa -out client.key 2048

//生成客户端的证书
openssl req -new -x509 -key client.key -out client.pem -days 365

go https 实现

Go和HTTPS

服务端

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w,
        "Hi, This is an example of https service in golang!")
}

func main() {
	http.HandleFunc("/", handler)
	//server.crt  服务端证书地址
	//server.key  服务端私钥地址
	http.ListenAndServeTLS(":8081", "server.crt", "server.key", nil)
}


让这个例子能先Run起来,我们在程序所在目录先执行下面命令,利用openssl生成server.crt和server.key文件,供程序使用.

openssl genrsa -out server.key 2048

openssl req -new -x509 -key server.key -out server.crt -days 365

显示为

$openssl genrsa -out server.key 2048

Generating RSA private key, 2048 bit long modulus
…………….+++
……………+++
e is 65537 (0×10001)

$openssl req -new -x509 -key server.key -out server.crt -days 365

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
—–
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:localhost   //这里注意输入
Email Address []:

然后 go run test.go 运行

在浏览器中输入 https://localhost:8081 , 忽略继续后,返回结果

或者使用 curl -k https://localhost:8081 测试

注意如果不加-k,curl会报如下错误:

$curl https://localhost:8081
curl: (60) SSL certificate problem: Invalid certificate chain
More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the –cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or –insecure) option.

客户端 :

package main

import (
    "crypto/tls"
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    tr := &http.Transport{
        TLSClientConfig:    &tls.Config{InsecureSkipVerify: true}, //跳过验证
    }
    client := &http.Client{Transport: tr}
    resp, err := client.Get("https://localhost:8081")

    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}

go tcp 证书使用

服务器证书的使用

服务端代码:

首先从上面我们创建的服务器私钥和pem文件中得到证书cert,并且生成一个tls.Config对象。这个对象有多个字段可以设置,本例中我们使用它的默认值。 然后用tls.Listen开始监听客户端的连接,accept后得到一个net.Conn,后续处理和普通的TCP程序一样。

package main
import (
	"bufio"
	"crypto/tls"
	"log"
	"net"
)
func main() {
	cert, err := tls.LoadX509KeyPair("server.pem", "server.key")
	if err != nil {
		log.Println(err)
		return
	}
	config := &tls.Config{Certificates: []tls.Certificate{cert}}
	ln, err := tls.Listen("tcp", ":443", config)
	if err != nil {
		log.Println(err)
		return
	}
	defer ln.Close()
	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Println(err)
			continue
		}
		go handleConn(conn)
	}
}
func handleConn(conn net.Conn) {
	defer conn.Close()
	r := bufio.NewReader(conn)
	for {
		msg, err := r.ReadString('\n')
		if err != nil {
			log.Println(err)
			return
		}
		println(msg)
		n, err := conn.Write([]byte("world\n"))
		if err != nil {
			log.Println(n, err)
			return
		}
	}
}

客户端代码:

InsecureSkipVerify用来控制客户端是否证书和服务器主机名。如果设置为true,则不会校验证书以及证书中的主机名和服务器主机名是否一致。 因为在我们的例子中使用自签名的证书,所以设置它为true,仅仅用于测试目的。

package main
import (
	"crypto/tls"
	"log"
)
func main() {
	conf := &tls.Config{
		InsecureSkipVerify: true,
	}
	conn, err := tls.Dial("tcp", "127.0.0.1:443", conf)
	if err != nil {
		log.Println(err)
		return
	}
	defer conn.Close()
	n, err := conn.Write([]byte("hello\n"))
	if err != nil {
		log.Println(n, err)
		return
	}
	buf := make([]byte, 100)
	n, err = conn.Read(buf)
	if err != nil {
		log.Println(n, err)
		return
	}
	println(string(buf[:n]))
}

客户端证书的使用

客户端:

package main
import (
	"crypto/tls"
	"log"
)
func main() {
	conf := &tls.Config{
		InsecureSkipVerify: true,
	}
	conn, err := tls.Dial("tcp", "127.0.0.1:443", conf)
	if err != nil {
		log.Println(err)
		return
	}
	defer conn.Close()
	n, err := conn.Write([]byte("hello\n"))
	if err != nil {
		log.Println(n, err)
		return
	}
	buf := make([]byte, 100)
	n, err = conn.Read(buf)
	if err != nil {
		log.Println(n, err)
		return
	}
	println(string(buf[:n]))
}

因为需要验证客户端,我们需要额外配置下面两个字段

ClientAuth:   tls.RequireAndVerifyClientCert,
ClientCAs:    clientCertPool,

然后客户端也配置这个clientCertPool

package main
import (
	"crypto/tls"
	"crypto/x509"
	"io/ioutil"
	"log"
)
func main() {
	cert, err := tls.LoadX509KeyPair("client.pem", "client.key")
	if err != nil {
		log.Println(err)
		return
	}
	certBytes, err := ioutil.ReadFile("client.pem")
	if err != nil {
		panic("Unable to read cert.pem")
	}
	clientCertPool := x509.NewCertPool()
	ok := clientCertPool.AppendCertsFromPEM(certBytes)
	if !ok {
		panic("failed to parse root certificate")
	}
	conf := &tls.Config{
		RootCAs:            clientCertPool,
		Certificates:       []tls.Certificate{cert},
		InsecureSkipVerify: true,
	}
	conn, err := tls.Dial("tcp", "127.0.0.1:443", conf)
	if err != nil {
		log.Println(err)
		return
	}
	defer conn.Close()
	n, err := conn.Write([]byte("hello\n"))
	if err != nil {
		log.Println(n, err)
		return
	}
	buf := make([]byte, 100)
	n, err = conn.Read(buf)
	if err != nil {
		log.Println(n, err)
		return
	}
	println(string(buf[:n]))
}

参考

SSL/TLS协议运行机制的概述

图解SSL/TLS协议

Go和HTTPS

自建证书配置HTTPS服务器

互联网全站HTTPS的时代已经到来

未完后续继续添加!!

转载于:https://my.oschina.net/solate/blog/1540652

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值