【GO】29.go-gin支持ssl/tls,即https示例

news2024/10/2 14:32:39

本文为演示采用自签名证书

一.生成证书

通过openssl工具生成证书

1.1 安装openssl

macos通过brew安装

brew install openssl

1.2 生成跟证书私钥

openssl genrsa -out ca.key 4096

1.3 准备配置文件

vim ca.conf

内容如下

[ req ]
default_bits       = 4096
distinguished_name = req_distinguished_name

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
countryName_default         = CN
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = JiangSu
localityName                = Locality Name (eg, city)
localityName_default        = NanJing
organizationName            = Organization Name (eg, company)
organizationName_default    = Sheld
commonName                  = Common Name (e.g. server FQDN or YOUR name)
commonName_max              = 64
commonName_default          = Ted CA Test

生成根证书签发申请文件(csr文件)

openssl req -new -sha256 -out ca.csr -key ca.key -config ca.conf

该命令含义如下:

req——执行证书签发命令

-new——新证书签发请求

-key——指定私钥路径

-out——输出的csr文件的路径

1.4 自签发根证书(cer文件)

openssl x509 -req -days 3650 -in ca.csr -signkey ca.key -out ca.crt

该命令的含义如下:

x509——生成x509格式证书

-req——输入csr文件

-days——证书的有效期(天)

-signkey——签发证书的私钥

-in——要输入的csr文件

-out——输出的cer证书文件

1.5 生成服务端私钥

openssl genrsa -out server.key 2048

1.6 准备配置文件,得到server.conf

vim server.conf

内容如下

[ req ]
default_bits       = 2048
distinguished_name = req_distinguished_name
req_extensions     = req_ext

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
countryName_default         = CN
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = JiangSu
localityName                = Locality Name (eg, city)
localityName_default        = NanJing
organizationName            = Organization Name (eg, company)
organizationName_default    = Sheld
commonName                  = Common Name (e.g. server FQDN or YOUR name)
commonName_max              = 64
commonName_default          = server.com

[ req_ext ]
subjectAltName = @alt_names

[alt_names]
DNS.1   = server.com

生成服务端证书申请文件

openssl req -new -sha256 -out server.csr -key server.key -config server.conf

参考1.3输入服务端证书信息

1.7 用CA证书签发服务端证书

openssl x509 -req -days 3650 -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt -extensions req_ext -extfile server.conf

这里有必要解释一下这几个参数:

-CA——指定CA证书的路径

-CAkey——指定CA证书的私钥路径

-CAserial——指定证书序列号文件的路径

-CAcreateserial——表示创建证书序列号文件(即上方提到的serial文件),创建的序列号文件默认名称为-CA,指定的证书名称后加上.srl后缀

1.8 生成客户端私钥

openssl genrsa -out client.key 2048

1.9 准备配置文件,得到client.conf

vim client.conf

内容如下

[ req ]
default_bits       = 2048
distinguished_name = req_distinguished_name
req_extensions     = req_ext

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
countryName_default         = CN
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = HeNan
localityName                = Locality Name (eg, city)
localityName_default        = AnYang
organizationName            = Organization Name (eg, company)
organizationName_default    = Sheld_client
commonName                  = Common Name (e.g. server FQDN or YOUR name)
commonName_max              = 64
commonName_default          = server.com

[ req_ext ]
subjectAltName = @alt_names

[alt_names]
DNS.1   = server.com
DNS.2    = localhost

生成客户端证书申请文件

openssl req -new -sha256 -out client.csr -key client.key -config client.conf

1.10 用跟证书签发客户端证书

openssl x509 -req -days 3650 -CA ca.crt -CAkey ca.key -CAserial ca.srl -in client.csr -out client.crt -extensions req_ext -extfile client.conf

需要注意的是,上方签发服务端证书时已经使用-CAcreateserial生成过ca.srl文件,因此这里不需要带上这个参数了。

二. gin框架实现https,服务端不需要验证客户端证书的示例

2.1 项目引入gin框架

go get -u github.com/gin-gonic/gin

2.2 服务端代码

package main

import (
    "github.com/gin-gonic/gin"
    "net/http"
)

func main() {
    router := gin.New()

    router.GET("/test", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "success",
        })
    })

    // 可以直接用
    //router.RunTLS("0.0.0.0:10679", "./certs/server.cer", "./certs/server.key")
    server := &http.Server{Addr: "0.0.0.0:10679", Handler: router}
    _ = server.ListenAndServeTLS("./certs/server.cer", "./certs/server.key")
}

2.3 安全提示

打开浏览器访问https://localhost:10679/test

因为浏览器并不信任证书的颁发机构,浏览器会有安全提示,当然可以点高级直接强行访问页面,同样可以返回结果,很多文章也是到这里就结束了。强迫症的我还是要解决一下。

2.4 给浏览器添加自己生成的CA证书

本文选择firefox浏览器,因为设置可以轻松添加信任CA证书。chrome则是直接掉起系统的证书,没有找到友好的办法自己添加

我们把CA证书(1.4节生成的ca.csr)添加到firefox证书颁发机构中去,这样浏览器就信任我们自制的CA证书了。路径为:设置->隐私与安全->查看证书->导入

由于我们证书配置的common name是server.com,因此需要修改本地hosts文件,将下面文字添加到hosts文件中

127.0.0.1 server.com

再次访问https://server.com:10679/test不再阻止

三.实现服务器和客户端双端验证

3.1 服务端代码

package main

import (
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "github.com/gin-gonic/gin"
    "io/ioutil"
    "log"
    "net"
    "net/http"
    "os"
    "os/signal"
    "syscall"
)

var (
    caCert     string = "./certs/ca.crt"
    serverCert string = "./certs/server.crt"
    serverKey  string = "./certs/server.key"
)

func main() {
    router := gin.New()
    router.Use(gin.Logger())

    router.GET("/test", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "success",
        })
    })

    // 客户端CA证书
    certPool := x509.NewCertPool()
    ca, err := os.ReadFile(caCert)
    if err != nil {
        fmt.Printf("load ca err: %s", err)
        return
    }

    if ok := certPool.AppendCertsFromPEM(ca); !ok {
        fmt.Printf("certpool append ca fail.")
        return
    }

    // 可以直接用注释的代码代替最后两行
    //router.RunTLS("0.0.0.0:10679", "./cert/server.cer", "./cert/server.key")
    server := &http.Server{
        Addr:    "server.com:10679",
        Handler: router,
        TLSConfig: &tls.Config{
            ClientAuth: tls.RequireAndVerifyClientCert,
            //这里一定要注意,服务端设置ClientCAs,用于服务端验证客户端证书,客户端设置RootCAs,用户客户端验证服务端证书。设置错误或者设置反了都会造成认证不通过。
            //RootCAs:    certPool,
            ClientCAs: certPool,
        },
    }

    _ = server.ListenAndServeTLS(serverCert, serverKey)
}

3.2 客户端代码

package main

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

func main() {
    pool := x509.NewCertPool()
    caCrt, err := os.ReadFile("./certs/ca.crt")
    if err != nil {
        log.Fatal("read ca.crt file error:", err.Error())
    }
    pool.AppendCertsFromPEM(caCrt)
    cliCrt, err := tls.LoadX509KeyPair("./certs/client.crt", "./certs/client.key")
    if err != nil {
        log.Fatalln("LoadX509KeyPair error:", err.Error())
    }
    tr := &http.Transport{
        TLSClientConfig: &tls.Config{
            //这里一定要注意,服务端设置ClientCAs,用于服务端验证客户端证书,客户端设置RootCAs,用户客户端验证服务端证书。设置错误或者设置反了都会造成认证不通过。
            RootCAs: pool,
            //ClientCAs:    pool,
            Certificates: []tls.Certificate{cliCrt},
        },
    }
    client := &http.Client{Transport: tr}
    resp, err := client.Get("https://server.com:10679/test")
    if err != nil {
        fmt.Printf("get failed. | err: %s\n", err)
        return
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Println(string(body))

}

四. 整理过程中遇到的bug

4.1 tls: failed to verify certificate: x509: “server.com” certificate is not standards compliant

remote error: tls: bad certificate

这两个报错可能就是客户端或服务端设置参数ClientCAs、RootCAs错误有关。

服务端设置ClientCAs,里面保存客户端的CA证书Pool,用于服务端验证客户端证书。

客户端设置RootCAs,里面保存服务端的CA证书Pool,用户客户端验证服务端证书。

设置错误或者设置反了都会造成认证不通过。

这就属于知道就很简单解决,但找不到错误就很崩溃,本人因为这个小bug竟然熬了一夜,说多了都是泪啊啊啊啊。。。

4.2 use SANs or temporarily enable Common Name matching with GODEBUG=x509ignoreCN=0

这个报错原因是生成证书没有开启SAN扩展,go 1.15 版本开始废弃 CommonName,因此推荐使用 SAN 证书。 这就是1.6、1.9两节配置文件最下面做的事情。重新把证书生成一下。当然,自己生成的证书可以随便改,如果线上证书出现这种情况,我看有的说设置下环境变量GODEBUG 为 x509ignoreCN=0,不过我测试没有效果

这个问题的解决要感谢下面这个博主,我是看了这个帖子解决的问题。

https://blog.csdn.net/weixin_40280629/article/details/113563351

4.3 安全提示

参考2.3解决办法

4.4 证书commonName

这个字段是比较重要的,不要随便配置。服务端、客户端证书认证都会验证host name是否与其一致,不一致会造成认证失败。这也是为什么要修改hosts并用server.com来访问

睡觉睡觉。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/337874.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

光耦合器的定义与概述

光耦合器或光电耦合器是一种电子元件,基本上充当具有不同电压电平的两个独立电路之间的接口。光耦合器是可在输入和输出源之间提供电气隔离的常用元件。它是一个 6 引脚器件,可以有任意数量的光电探测器。 在这里,光源发出的光束作为输入和输…

设计模式第7式:适配器模式与外观模式

前言 前面讲的装饰者模式是将对象包装起来,并赋予新的职责。适配器模式同样是包装对象,但是目的不一样,它要让某些对象的接口看起来不像自己而是像别的东西。为什么要这样做,因为可以将类的接口转换成想要的接口。还会讲一个适配…

C++中的枚举与位域

枚举在传统 C中,枚举类型并非类型安全,枚举类型会被视作整数,则会让两种完全不同的枚举类型可以进行直接的比较(虽然编译器给出了检查,但并非所有),甚至同一个命名空间中的不同枚举类型的枚举值…

GPR后期功能整理

基金本子写得太困难了,学术水平不够,好的想法未想到好的科学问题,难以下笔。和龙工沟通后,得到了大量impulse radar的数据,后期需要进行分析,从而能让GPR智能识别走得更远。从数据解译角度,找到…

配置CMAKE编译环境:VSCODE + MinGW

一. MinGW安装 MinGW(Minimalist GNU For Windows)是个精简的Windows平台C/C、ADA及Fortran编译器,相比Cygwin而言,体积要小很多,使用较为方便。 MinGW最大的特点就是编译出来的可执行文件能够独立在Windows上运行。 MinGW的组成&#xff…

[Linux]进程替换

🥁作者: 华丞臧. 📕​​​​专栏:【LINUX】 各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞收藏关注)。如果有错误的地方,欢迎在评论区指出。 推荐一款刷题网站 👉 LeetCode刷题网站 文…

HashMap设计思想学习

HashMap设计思想学习引言树化与退化红黑树的优势索引计算put流程扩容(加载)因子为何默认是 0.75fhashMap并发丢失数据问题jdk 1.7并发死链问题key 的设计引言 hashmap在jdk 1.7之前是数组链表结构,而jdk1.8之后变为是数组(链表|红黑树) 树化…

【第38天】不同路径数问题 | 网格 dp 入门

本文已收录于专栏🌸《Java入门一百例》🌸学习指引序、专栏前言一、网格模型二、【例题1】1、题目描述2、解题思路3、模板代码4、代码解析5.原题链接三、【例题2】1、题目描述2、解题思路3、模板代码4、代码解析5.原题链接三、推荐专栏四、课后习题序、专…

大型物流运输管理系统源码 TMS源码

大型物流运输管理系统源码 TMS是一套适用于物流公司的物流运输管理系统,涵盖物流公司内部从订单->提货->运单->配车->点到->预约->签收->回单->代收货款的全链条管理系统。 菜单功能 一、运营管理 1、订单管理:用于客户意向订…

ChatGPT 到底强大在哪里?(文末有彩蛋)

ChatGPT 是由 OpenAI 开发的一个人工智能聊天机器人程序,于2022年11月推出。该程序使用基于 GPT-3.5 架构的大型语言模型并通过强化学习进行训练。ChatGPT 以文字方式交互,而除了可以通过人类自然对话方式进行交互,还可以用于相对复杂的语言工…

一定要收藏的面试思维导图,粉丝分享面试经验

一位粉丝分享面试经验:1.常见面试题有哪些?主要从以下一些知识点做了准备: 常用的分析方法、Excel、SQL、 A/B测试、产品分析。然后每份面试针对职位要求,还有前期和HR聊天一点点了解这个职位之后,定向准备。 Excel、S…

OpenMMLab 实战营打卡 - 第 7 课

OpenMMLab MMSegmentation内容概要MMSegmentation统一超参MMSegmentation 的项目结构分割模型的模块化设计分割模型的配置文件主干网络的配置ResNet v1c主解码头的配置辅助解码头的配置数据集配置数据处理流水线常用训练策略参考资料内容概要 • MMSegmentation 项目概述 • M…

TSDF算法应用与源码详解

数据与代码链接见文末 1.TSDF整体概述 (1)需要准备的输入 1.原始图片;2.对应的深度信息;3.每张图的相机位姿;4.相机内参 原始输入图像数据(就是一个场景多个视角拍摄的结果) 输入图像的深度信息,位姿信息等(一般由相机得到,也可以通过算法得到) (2)整体概述…

【DFS并查集】岛屿数量

经典的dfs/bfs问题,给一个起点开始搜索,满足条件则继续调用dfs/bfs 从没有访问过的某个陆地出发,将所有能到达的陆地的状态都记录为已访问。下次出发不从已访问的陆地出发,每次出发前都把岛屿数 1即可 class Solution { public…

STM32开发(9)----CubeMX配置外部中断

CubeMX配置外部中断前言一、什么是中断1.STM32中断架构体系2.外部中断/事件控制器(EXTI)3.嵌套向量中断控制器(NIVC)二、实验过程1.CubeMX配置2.代码实现3.硬件连接4.实验结果总结前言 本章介绍使用STM32CubeMX对引脚的外部中断进…

MySQL 3:MySQL数据库基本操作 DQL

数据库管理系统的一个重要功能是数据查询。数据查询不应简单地返回数据库中存储的数据&#xff0c;还应根据需要对数据进行过滤&#xff0c;确定数据的显示格式。MySQL 提供了强大而灵活的语句来实现这些操作。MySQL数据库使用select语句查询数据。 select [all|distinct]<…

09- 机器学习经典流程 (中国人寿保费项目) (机器学习)

删除特征: data data.drop([region, sex], axis1)特征数据调整: data.apply( ) # 体重指数&#xff0c;离散化转换&#xff0c;体重两种情况&#xff1a;标准、肥胖 def convert(df,bmi):df[bmi] fat if df[bmi] > bmi else standardreturn df data data.apply(convert, …

EXCEL-职业版本(1)

EXCEL职业版本(1) 工作表 插入 注&#xff1a;默认会在鼠标选中的sheet后面新增&#xff0c;例如图中&#xff0c;当前选中的是sheet2&#xff0c;点击新增后会在sheet2后面自动新增一个sheet 删除 移动或者复制 类似于copy一整个sheet的所有内容 step1 右击【sheet名称】选…

Cordova

一、简介 Cordova 是用 Web 技术&#xff08; HTML&#xff0c;CSS 和 JS &#xff09;构建移动应用的平台。我们可以认为Cordova 是一个容器&#xff0c;用于将的 Web 应用移植到移动端&#xff0c;同时支持移动端的功能&#xff08;例如&#xff1a;定位、蓝牙、摄像头等&am…

Linux内核并发与竞争-原子操作

一.原子操作的概念首先看一下原子操作&#xff0c;原子操作就是指不能再进一步分割的操作&#xff0c;一般原子操作用于变量或者位操作。假如现在要对无符号整形变量 a 赋值&#xff0c;值为 3&#xff0c;对于 C 语言来讲很简单&#xff0c;直接就是&#xff1a; a3但是 C 语言…