白话https之golang实现https双向认证

_二师兄_ · · 7320 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

一、什么是https?

日常开发中大家可能接触最多的都是http协议,说到http协议也不得不提到TCP/IP协议以及计算机网络中tcp/ip五层与OCI7层架构模型有关,http(超文本传输协议)用户客户端和服务端之间的通信,位于tcp/ip五层协议中最上层传输层,http传输过程中都是明文传输,易引起安全问题,所有诞生了https协议。

HTTPS协议 = HTTP协议 + SSL/TLS协议,在HTTPS数据传输的过程中,需要用SSL/TLS对数据进行加密和解密,需要用HTTP对加密后的数据进行传输,由此可以看出HTTPS是由HTTP和SSL/TLS一起合作完成的。

SSL的全称是Secure Sockets Layer,即安全套接层协议,是为网络通信提供安全及数据完整性的一种安全协议。SSL协议在1994年被Netscape发明,后来各个浏览器均支持SSL,其最新的版本是3.0
https通信过程如下图所示:


image.png

二、加密算法

2.1 TLS/SSL的功能主要依赖三类算法实现:散列函数 Hash、对称加密和非对称加密,其利用非对称加密实现身份认证和密钥协商,对称加密算法采用协商的密钥对数据加密,基于散列函数验证信息的完整性(对于三种加密算法含义这里就不一一概述),而在TLS/SSL加密算法中还会涉及一下几个关键概念:

  • 密钥:改变密码行为的数字化参数。
  • 对称密钥加密系统:编 / 解码使用相同密钥的算法。
  • 不对称密钥加密系统:编 / 解码使用不同密钥的算法。
  • 公开密钥加密系统:一种能够使数百万计算机便捷地发送机密报文的系统。
  • 数字签名:用来验证报文未被伪造或篡改的校验和。
  • 数字证书:由一个可信的组织验证和签发的识别信息。
    2.2 TLS的基本工作方式是,客户端使用非对称加密与服务器进行通信,实现身份验证并协商对称加密使用的密钥, 然后对称加密算法采用协商密钥对信息以及信息摘要进行加密通信,不同的节点之间采用的对称密钥不同,从而可以保证信息只能通信双方获取。
    2.3 https通信过程中数字证书概览:


    image.png

    ca.pem 根证书,由根证书颁发客户端和服务端证书
    client.pem 客户端证书
    client-key.pem 客户端秘钥
    server.pem 服务端证书
    server-key.pem 服务端秘钥

三、如何自签名证书?

3.1 目前自颁发证书工具有两类,一是使用openssl 插件生成证书,二是通过cfssl插件生成证书,本例采用cfssl 工具生成证书

3.2 cfssl是一款由golang编写的证书生成工具,官网地址:https://github.com/cloudflare/cfssl
安装步骤:

@tips: 该方法需要安装golang环境,如果没有golang请参照二进制包安装方式
go get -u github.com/cloudflare/cfssl/cmd/cfssl
go get -u github.com/cloudflare/cfssl/cmd/cfssljson
@tips windows环境需要将gopath下bin目录添加path环境中方可执行cfssl命令
查看安装是否成功:
cfssl
image.png

3.3 创建CA证书

mkdir ssl & cd ssl
cfssl print-defaults config > ca-config.json
cfssl print-defaults csr > ca-csr.json
然后修改ca-config.json文件
vi ca-config.json
{
    "signing": {
        "default": {
            "expiry": "43800h"
        },
        "profiles": {
            "server": {
                "expiry": "43800h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth"
                ]
            },
            "client": {
                "expiry": "43800h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "client auth"
                ]
            },
            "peer": {
                "expiry": "43800h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth",
                    "client auth"
                ]
            }
        }
    }
}
## 生成ca 证书
mkdir ca
cfssl gencert -initca ca-csr.json | cfssljson -bare ca/ca -

3.4 创建serverd端证书

mkdir server
cfssl print-defaults csr > server.json
## 修改server.json
{
    "CN": "Server",
    "hosts": [
            "localhost",
            "127.0.0.1"
       ],
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "names": [
        {
            "C": "CN",
            "L": "SH",
            "ST": "SH"
        }
    ]
}
## 签发server端证书
cfssl gencert -ca=ca/ca.pem -ca-key=ca/ca-key.pem -config=ca-config.json -profile=www server/server.json | cfssljson -bare server/server

3.5 创建客户端证书:

mkdir client
cfssl print-defaults csr > client.json
## 修改client.json
{
    "CN": "Client",
    "hosts": [],
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "names": [
        {
            "C": "CN",
            "L": "SH",
            "ST": "SH"
        }
    ]
}
## 生成客户端证书和私钥
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client client.json | cfssljson -bare client/client

3.6 检验生成的证书是否和配置相符合:

openssl x509 -in ca.pem -text -noout
openssl x509 -in server.pem -text -noout
openssl x509 -in client.pem -text -noout

四 golang实现https双向认证

server端代码实现:

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

func main() {
  // ssl 双向检验
    pool := x509.NewCertPool()
    crt, err := ioutil.ReadFile(cert.K8sAgentCrtPath)
    if err != nil {
        log.Fatalln("读取证书失败!", err.Error())
    }
    pool.AppendCertsFromPEM(crt)
    http.HandleFunc("/", handler)
    s := &http.Server{
        Addr: ":8080",
        TLSConfig: &tls.Config{
            ClientCAs:  pool,
                        ClientAuth: tls.RequireAndVerifyClientCert,  // 检验客户端证书
                      
        },
    }
    log.Fatal(s.ListenAndServeTLS(cert.K8sAgentServerCertPath, cert.K8sAgentKeyPath))
}

客户端代码实现:

func main() {
    pool := x509.NewCertPool()
    caCrt, err := ioutil.ReadFile(cert.K8sAgentCrtPath)
    if err != nil {
        log.Fatal("read ca.crt file error:", err.Error())
    }
    pool.AppendCertsFromPEM(caCrt)
    cliCrt, err := tls.LoadX509KeyPair(cert.ClientCrt, cert.ClientKey)
    if err != nil {
        log.Fatalln("LoadX509KeyPair error:", err.Error())
    }
    tr := &http.Transport{
        TLSClientConfig: &tls.Config{
            RootCAs:      pool,
            Certificates: []tls.Certificate{cliCrt},
        },
    }
    client := &http.Client{Transport: tr}
    resp, err := client.Get("https://127.0.0.1:8080/")
    if err != nil {
        panic(err.Error())
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))
}

跨语言调用,node 客户端:

const https = require('https');
const fs = require('fs');

const options = {
    hostname: '127.0.0.1',
    port: 8080,
    method: 'get',
    key: fs.readFileSync('./cert/clent-key.pem'),
    cert: fs.readFileSync('./cert/clent-key.pem'),
    ca:[fs.readFileSync('./cert/clent-key.pem')],
    agent: false,
    rejectUnauthorized: true
};

const req = https.request(options, (res) => {
    const certificate = res.connection.getPeerCertificate();
    const rawDir = certificate.raw;
    console.log(rawDir);
    console.log('Client connected', res.connection.authorized ? 'authorized': 'unauthorized');
    console.log('状态码:', res.statusCode);
    console.log('请求头:', res.headers);
    res.setEncoding('utf-8');
    res.on('data', (d) => {
        process.stdout.write(d);
    });
});

req.on('error', (e) =>{
    console.error(e);
});

req.end();

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:_二师兄_

查看原文:白话https之golang实现https双向认证

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

7320 次点击  
加入收藏 微博
上一篇:2020-04-23
下一篇:docker学习记录
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传