前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用https双端互相认证实现设备公网接入

使用https双端互相认证实现设备公网接入

原创
作者头像
海阔天空0
发布2019-02-23 20:27:54
1.7K0
发布2019-02-23 20:27:54
举报
文章被收录于专栏:httpshttps

背景

在公网环境下,设备接入要保证安全性,server端既要验证设备的身份,设备也要验证server端的身份,这时就需要做双端互相认证。

我们先用网页的https单向认证举例,来说明证书是如何验证的。

1. 单向身份认证

一般的HTTPS服务都是只需要客户端验证服务器的身份就好了。比如我们想访问某个网站,我们得确认那个网站真是我们要访问的网站,而不是一个界 面类似的用来诱骗我们帐号的钓鱼网站。而网站并不需要通过TLS验证我们的身份。

https服务端程序

package main

import (
	"io"
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		io.WriteString(w, "hello, world!\n")
	})
	if e := http.ListenAndServeTLS(":443", "server.crt", "server.key", nil); e != nil {
		log.Fatal("ListenAndServe: ", e)
	}
}

通过openssl创建自签名证书

openssl genrsa -out server.key 2048
openssl req -nodes -new -key server.key -subj "/CN=localhost" -out server.csr
openssl x509 -req -sha256 -days 365 -in server.csr -signkey server.key -out server.crt

客户端程序

让客户端用服务器自己的证书证验证它自己。类似于curl --cacert server.crt

package main

import (
	"crypto/tls"
	"io"
	"log"
	"net/http"
	"os"
)

func loadCrt(caFile string) *x509.CertPool {
	pool := x509.NewCertPool()

	if ca, e := ioutil.ReadFile(caFile); e != nil {
		log.Fatal("ReadFile: ", e)
	} else {
		pool.AppendCertsFromPEM(ca)
	}
	return pool
}

func main() {
	c := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{RootCAs: loadCrt("server.crt")},
		}}

	if resp, e := c.Get("https://localhost"); e != nil {
		log.Fatal("http.Client.Get: ", e)
	} else {
		defer resp.Body.Close()
		io.Copy(os.Stdout, resp.Body)
	}
}

2. 双方认证对方身份

设备作为后台接入需要验证的时候,我们希望双方都利用一个身份证(certificate)通过TLS协议向对方展示自己的身份,而不是像人一下输入帐号、密码。

创建CA并签署server以及client的身份证

我们可以按照上文中例子展示的:让通信双方互相交换身份证,这样既可互相验证。但是如果一个系统里有多方, 任意两方都要交换身份太麻烦。我们通常创建一个自签署的根身份证,然后用它来签署系 统中各方的身份。这样每一方都只要有这个根身份即可验证所有其他通信方。

这里解释了用OpenSSL生成根身份证和签署其他身 份证的过程。针对我们的例子,具体过程如下:

创建我们自己CA的私钥:

openssl genrsa -out ca.key 2048

创建我们自己CA的CSR,并且用自己的私钥自签署之,得到CA的身份证:

openssl req -x509 -new -nodes -key ca.key -days 365 -out ca.crt -subj "/CN=me"

创建server的私钥,CSR,并且用CA的私钥自签署server的身份证:

openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/CN=localhost"
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365

创建client的私钥,CSR,以及用ca.key签署client的身份证:

openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj "/CN=localhost"
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365

Server

相对于上面的例子,server需要做一些修改: 增加了一个 http.Server 变量serv,并且调用serv.ListenAndServeTLS,而不 是像之前那样直接调用http.ListenAndServeTLS了:

func main() {
	serv := &http.Server{
		Addr: ":443",
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintf(w, "Hello World!\n")
		}),
		TLSConfig: &tls.Config{
			ClientCAs:  loadCA("ca.crt"),
			ClientAuth: tls.RequireAndVerifyClientCert,
		},
	}

	e := serv.ListenAndServeTLS("server.crt", "server.key")
	if e != nil {
		log.Fatal("ListenAndServeTLS: ", e)
	}
}

Client

客户端程序相对于上面的变化主要在于

调用tls.LoadX509KeyPair读取client.key和client.crt,并返回一个 tls.Certificate变量,

把这个变量传递给http.Client变量,然后调用其Get函数。

func main() {
	pair, e := tls.LoadX509KeyPair("client.crt", "client.key")
	if e != nil {
		log.Fatal("LoadX509KeyPair:", e)
	}

	client := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				RootCAs:      loadCA("ca.crt"),
				Certificates: []tls.Certificate{pair},
			},
		}}

	resp, e := client.Get("https://localhost")
	if e != nil {
		log.Fatal("http.Client.Get: ", e)
	}
	defer resp.Body.Close()
	io.Copy(os.Stdout, resp.Body)
}

运行和测试

setsid go run ./server.go 
go run ./client.go

屏幕上打印出Hello World!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 1. 单向身份认证
    • https服务端程序
      • 客户端程序
      • 2. 双方认证对方身份
        • 创建CA并签署server以及client的身份证
          • Server
            • Client
              • 运行和测试
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档