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

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

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


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

查看所有标签

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

算法(第4版)

算法(第4版)

塞奇威克 (Robert Sedgewick)、韦恩 (Kevin Wayne) / 谢路云 / 人民邮电出版社 / 2012-10-1 / 99.00元

本书全面讲述算法和数据结构的必备知识,具有以下几大特色。  算法领域的经典参考书 Sedgewick畅销著作的最新版,反映了经过几十年演化而成的算法核心知识体系  内容全面 全面论述排序、搜索、图处理和字符串处理的算法和数据结构,涵盖每位程序员应知应会的50种算法  全新修订的代码 全新的Java实现代码,采用模块化的编程风格,所有代码均可供读者使......一起来看看 《算法(第4版)》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

MD5 加密
MD5 加密

MD5 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具