前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go https 笔记

go https 笔记

作者头像
solate
发布2019-07-22 17:54:27
8050
发布2019-07-22 17:54:27
举报
文章被收录于专栏:solate 杂货铺solate 杂货铺

理解为运行在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
代码语言:javascript
复制
echo $PATH //打印环境变量

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

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

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

  1. 修改 $PATH
代码语言:javascript
复制
echo $SHELL   //确认自己使用的shell

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

或

/bin/zsh  //修改 ~/.zshrc
  1. homebrew 安装和软连接
代码语言:javascript
复制
brew install openssl

brew link openssl

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

brew link openssl --farce
  1. 新打开终端, 查看版本
代码语言:javascript
复制
openssl version -a

证书生成

服务器端的证书生成

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

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

//生成服务器端证书
openssl req -new -x509 -key server.key -out server.pem -days 365
客户端的证书生成
代码语言:javascript
复制
//生成客户端的私钥
openssl genrsa -out client.key 2048

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

go https 实现

Go和HTTPS

服务端

代码语言:javascript
复制
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文件,供程序使用.

代码语言:javascript
复制
openssl genrsa -out server.key 2048

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

显示为

代码语言:javascript
复制
$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 测试

代码语言:javascript
复制
注意如果不加-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.

客户端 :

代码语言:javascript
复制
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程序一样。

代码语言:javascript
复制
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,仅仅用于测试目的。

代码语言:javascript
复制
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]))
}

客户端证书的使用

客户端:

代码语言:javascript
复制
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]))
}

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

代码语言:javascript
复制
ClientAuth:   tls.RequireAndVerifyClientCert,
ClientCAs:    clientCertPool,

然后客户端也配置这个clientCertPool

代码语言:javascript
复制
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]))
}

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SSL和TLS
  • 自建证书
    • mac 安装升级openssl
      • 证书生成
        • 服务器端的证书生成
        • 客户端的证书生成
    • go https 实现
      • 服务端
        • 客户端 :
        • go tcp 证书使用
          • 服务器证书的使用
            • 客户端证书的使用
            相关产品与服务
            SSL 证书
            腾讯云 SSL 证书(SSL Certificates)为您提供 SSL 证书的申请、管理、部署等服务,为您提供一站式 HTTPS 解决方案。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档