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

栏目: 服务器 · 发布时间: 7年前

内容简介:在公网环境下,设备接入要保证安全性,server端既要验证设备的身份,设备也要验证server端的身份,这时就需要做双端互相认证。我们先用网页的https单向认证举例,来说明证书是如何验证的。一般的HTTPS服务都是只需要客户端验证服务器的身份就好了。比如我们想访问某个网站,我们得确认那个网站真是我们要访问的网站,而不是一个界 面类似的用来诱骗我们帐号的钓鱼网站。而网站并不需要通过TLS验证我们的身份。

背景

在公网环境下,设备接入要保证安全性,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!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Fluent Python

Fluent Python

Luciano Ramalho / O'Reilly Media / 2015-8-20 / USD 39.99

Learn how to write idiomatic, effective Python code by leveraging its best features. Python's simplicity quickly lets you become productive with it, but this often means you aren’t using everything th......一起来看看 《Fluent Python》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具