go入門實踐五-實現一個https服務

文章目錄

  • 前言
  • 生成證書
    • 申請免費的證書
    • 使用Go語言生成自簽CA證書
  • https的客戶端和服務端
    • 服務端代碼
    • 客戶端代碼
  • tls的客戶端和服務端
    • 服務端
    • 客戶端

前言

在公網中,我想加密傳輸的數據。(1)很自然,我想到了把數據放到http的請求中,然后通過tls確保數據安全。(2)更進一步,只要數據可以解析,則無需http協議,直接通過tls協議加密傳輸即可。本文分別嘗試了這兩個方案。

嘗試實現方案之前,我們考慮需要實現哪些內容。(1)如何獲取證書。(2)golang中如何實現一個https的客戶端和服務器。(3)golang中如何實現一個tls的客戶端和服務器。(4)http的request和response的構建,發送和解析。(5)對于客戶端, 應用層(http)是否應該復用網絡層(tcp)的連接; 哪些需求下不能復用; (6)不考慮傳輸層的網絡細節。

注:本文不涉及相關內容的背景知識介紹。本文完整代碼見倉庫。


生成證書

如果有已經購買的域名,可以申請一個免費的通配符證書,便于日常使用。

沒有域名的話:可以通過命令行生成證書,見:windows和linux上證書的增刪查。也可以通過go代碼來創建證書。


申請免費的證書

首先是安裝acme.sh

sudo apt-get -y install socatda1234cao@vultr:~$ curl https://get.acme.sh | sh -s email=da1234cao@163.com% Total    % Received % Xferd  Average Speed   Time    Time     Time  CurrentDload  Upload   Total   Spent    Left  Speed
100  1032    0  1032    0     0   4384      0 --:--:-- --:--:-- --:--:--  4410% Total    % Received % Xferd  Average Speed   Time    Time     Time  CurrentDload  Upload   Total   Spent    Left  Speed
100  216k  100  216k    0     0   715k      0 --:--:-- --:--:-- --:--:--  715k
[Fri Aug 11 06:17:18 AM UTC 2023] Installing from online archive.
[Fri Aug 11 06:17:18 AM UTC 2023] Downloading https://github.com/acmesh-official/acme.sh/archive/master.tar.gz
[Fri Aug 11 06:17:19 AM UTC 2023] Extracting master.tar.gz
[Fri Aug 11 06:17:19 AM UTC 2023] Installing to /home/da1234cao/.acme.sh
[Fri Aug 11 06:17:19 AM UTC 2023] Installed to /home/da1234cao/.acme.sh/acme.sh
[Fri Aug 11 06:17:19 AM UTC 2023] Installing alias to '/home/da1234cao/.bashrc'
[Fri Aug 11 06:17:19 AM UTC 2023] OK, Close and reopen your terminal to start using acme.sh
[Fri Aug 11 06:17:19 AM UTC 2023] Installing cron job
8 0 * * * "/home/da1234cao/.acme.sh"/acme.sh --cron --home "/home/da1234cao/.acme.sh" > /dev/null
[Fri Aug 11 06:17:19 AM UTC 2023] Good, bash is found, so change the shebang to use bash as preferred.
[Fri Aug 11 06:17:20 AM UTC 2023] OK
[Fri Aug 11 06:17:20 AM UTC 2023] Install success!

為了使用方便,我這里申請一個泛域名證書。我的域名是在阿里云購買的,所以本文僅嘗試獲取阿里云的泛域名證書。

參考:阿里云域名使用ACME自動申請免費的通配符https域名證書、acme.sh 使用泛域名|阿里云DNS |免費申請證書。

大概過程是:AccessKey管理->創建子用戶->允許open API訪問->添加DNS管理權限。將獲取到的AccessKey 和 Secret 寫到acme.sh.env配置文件里面。

export Ali_Key="*****"
export Ali_Secret="*******"

執行source ~/.bashrc

然后開始申請證書。

sudo ufw status
sudo ufw allow 80# --debug 參數查看執行過程
# 沒有web服務,80端口空閑, acme.sh 還能假裝自己是一個webserver, 臨時聽在80 端口, 完成驗證
## 如果執行報錯;稍等等會再嘗試;
acme.sh --issue --dns dns_ali -d *.da1234cao.top --standalone --debug[Fri Aug 11 07:07:43 AM UTC 2023] Your cert is in: /home/da1234cao/.acme.sh/*.da1234cao.top_ecc/*.da1234cao.top.cer
[Fri Aug 11 07:07:43 AM UTC 2023] Your cert key is in: /home/da1234cao/.acme.sh/*.da1234cao.top_ecc/*.da1234cao.top.key
[Fri Aug 11 07:07:43 AM UTC 2023] The intermediate CA cert is in: /home/da1234cao/.acme.sh/*.da1234cao.top_ecc/ca.cer
[Fri Aug 11 07:07:43 AM UTC 2023] And the full chain certs is there: /home/da1234cao/.acme.sh/*.da1234cao.top_ecc/fullchain.cer
[Fri Aug 11 07:07:43 AM UTC 2023] _on_issue_success
# 查看證書信息
openssl x509 -noout -text -in '*.da1234cao.top.cer'Signature Algorithm: ecdsa-with-SHA384Issuer: C = AT, O = ZeroSSL, CN = ZeroSSL ECC Domain Secure Site CAValidityNot Before: Aug 11 00:00:00 2023 GMTNot After : Nov  9 23:59:59 2023 GMTSubject: CN = *.da1234cao.top  <-----# 查看key信息
openssl ec -noout -text -in '*.da1234cao.top.key'
read EC key
Private-Key: (256 bit)

使用Go語言生成自簽CA證書

這里有比較詳細的介紹:使用Go語言生成自簽CA證書

package certificateimport ("crypto/rand""crypto/rsa""crypto/x509""crypto/x509/pkix""encoding/pem""io/ioutil""math/big""time"
)func Gencertificate(output string) error {// ref: https://foreverz.cn/go-cert// 生成私鑰priv, err := rsa.GenerateKey(rand.Reader, 2048)if err != nil {return err}// x509證書內容var csr = &x509.Certificate{Version:      3,SerialNumber: big.NewInt(time.Now().Unix()),Subject: pkix.Name{Country:            []string{"CN"},Province:           []string{"Shanghai"},Locality:           []string{"Shanghai"},Organization:       []string{"httpsDemo"},OrganizationalUnit: []string{"httpsDemo"},CommonName:         "da1234cao.top",},NotBefore:             time.Now(),NotAfter:              time.Now().AddDate(1, 0, 0),BasicConstraintsValid: true,IsCA:                  false,KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},}// 證書簽名certDer, err := x509.CreateCertificate(rand.Reader, csr, csr, priv.Public(), priv)if err != nil {return err}// 二進制證書解析interCert, err := x509.ParseCertificate(certDer)if err != nil {return err}// 證書寫入文件pemData := pem.EncodeToMemory(&pem.Block{Type:  "CERTIFICATE",Bytes: interCert.Raw,})if err = ioutil.WriteFile(output+"cert.pem", pemData, 0644); err != nil {panic(err)}// 私鑰寫入文件keyData := pem.EncodeToMemory(&pem.Block{Type:  "EC PRIVATE KEY",Bytes: x509.MarshalPKCS1PrivateKey(priv),})if err = ioutil.WriteFile(output+"key.pem", keyData, 0644); err != nil {return err}return nil
}

https的客戶端和服務端

輪子已經有了,net/http。我沒有看到net/http很好的入門教程。只能看下官方文檔,網上翻翻一些簡單的示例。

下面是一個示例。其中,必須一提的是,http請求結束后,連接可以仍然存在,放到閑置的連接池中。便于后續請求,復用之前的連接。我到源碼里面去看了下,沒太看懂,可見:Golang Http RoundTrip解析。


服務端代碼

啟動服務端后,對于/reflect路徑的request,構建一個response。注意其中的header和body內容的填充。

func reflect(w http.ResponseWriter, r *http.Request) {log.Println("handle reflect")w.Header().Set("Content-Type", "text/plain; charset=utf-8")w.WriteHeader(http.StatusOK)bodyByte, _ := io.ReadAll(r.Body)log.Println("recv:", string(bodyByte))w.Write(bodyByte)
}func Start() error {listenPort := Conf.ListenPortlistenIp := Conf.ListenIpif listenPort <= 0 || listenPort > 65535 {log.Println("invalid listen port:", listenPort)return errors.New("invalid listen port")}http.HandleFunc("/reflect", reflect)err := http.ListenAndServeTLS(listenIp+":"+strconv.Itoa(listenPort), Conf.Protocol.Https.Certificate, Conf.Protocol.Https.Key, nil)return err
}

客戶端代碼

客戶端可以選擇是否驗證服務端的證書。驗證證書不重要,因為我信任這個域名解析過程(如果DNS沒有被污染的話)。數據可以加密傳輸即可。代碼中使用http.NewRequest構建一個請求,然后通過http.Client.Do發送請求(可能會復用之前的連接)。

func New() *http.Client {cli := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: Conf.SkipVerify},},}return cli
}func DoRequest(cli *http.Client, data []byte) (*http.Response, error) {req, err := http.NewRequest("POST", Conf.Protocol+"://"+Conf.ServerIp+":"+strconv.Itoa(Conf.ServerPort)+"/reflect", bytes.NewBuffer(data))if err != nil {log.Println("fail to consstruct request", err)}return cli.Do(req)
}func PrintResponse(resp *http.Response, err error) {if err == nil {if resp.StatusCode == http.StatusOK {body, _ := ioutil.ReadAll(resp.Body)log.Print(string(body))}log.Println("http status code:", resp.StatusCode)} else {log.Print(err)}
}

tls的客戶端和服務端

當我們的應用層不需要http協議,只需要對應用層的數據進行加密傳輸。我們嘗試下面的代碼(下面代碼中,我手動構建了http的request和response,是為了保證接收到完整的數據后再處理,僅此而已)。 使用的庫是crypto/tls

服務端

func TLSDataHandle(conn net.Conn) {for {// 讀取requestioBuf := bufio.NewReader(conn)req, err := http.ReadRequest(ioBuf)if err != nil {log.Println(err)return}defer req.Body.Close()defer conn.Close()bodyByte, _ := io.ReadAll(req.Body)log.Println("recv: ", string(bodyByte))// 構建一個responsebuf := bytes.NewBuffer(nil)buf.WriteString("HTTP/1.1 200 OK\r\n")buf.WriteString("Content-Length: " + strconv.Itoa(len(bodyByte)) + "\r\n")buf.WriteString("\r\n")buf.Write(bodyByte)// 發送responsebuf.WriteTo(conn)}
}func TLSStart() error {listenPort := Conf.ListenPortlistenIp := Conf.ListenIpif listenPort <= 0 || listenPort > 65535 {log.Println("invalid listen port:", listenPort)return errors.New("invalid listen port")}cert, err := tls.LoadX509KeyPair(Conf.Protocol.Https.Certificate, Conf.Protocol.Https.Key)if err != nil {log.Println("fail to laod x509 key pair", err)}config := &tls.Config{Certificates: []tls.Certificate{cert}}listener, _ := tls.Listen("tcp", listenIp+":"+strconv.Itoa(listenPort), config)for {conn, _ := listener.Accept()go TLSDataHandle(conn)}
}

客戶端

func NewTlsConn() (net.Conn, error) {config := &tls.Config{InsecureSkipVerify: Conf.SkipVerify}return tls.Dial("tcp", Conf.ServerIp+":"+strconv.Itoa(Conf.ServerPort), config)
}func SendRequest(conn net.Conn, data []byte) {// 構造一個請求buf := bytes.NewBuffer(nil)buf.WriteString("POST /no_thing")buf.WriteString(" HTTP/1.1\r\n")buf.WriteString("Content-Length: " + strconv.Itoa(len(data)) + "\r\n")buf.WriteString("\r\n")buf.Write(data)// 發送請求buf.WriteTo(conn)// 讀取回復ioBuf := bufio.NewReader(conn)res, err := http.ReadResponse(ioBuf, nil)if err != nil {log.Println(err)return}defer res.Body.Close()bodyByte, _ := io.ReadAll(res.Body)log.Println(string(bodyByte))
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/41012.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/41012.shtml
英文地址,請注明出處:http://en.pswp.cn/news/41012.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2023年京東寵物食品行業數據分析(京東大數據)

寵物食品市場需求主要來自于養寵規模&#xff0c;近年來由于我國寵物數量及養寵人群的規模均在不斷擴大&#xff0c;寵物相關產業和市場規模也在蓬勃發展&#xff0c;寵物食品市場也同樣保持正向增長。 根據鯨參謀電商數據分析平臺的相關數據顯示&#xff0c;2023年1月-7月&am…

vue5種模糊查詢方式

在Vue中&#xff0c;有多種方式可以實現模糊查詢。以下是五種常見的模糊查詢方式&#xff1a; 使用JavaScript的filter()方法&#xff1a;使用filter()方法可以對數組進行篩選&#xff0c;根據指定的條件進行模糊查詢。例如&#xff1a; data() {return {items: [{ name: App…

接口自動化測試(添加課程接口調試,調試合同上傳接口,合同列表查詢接口,批量執行)

1、我們把信息截取一下 1.1 添加一個新的請求 1.2 對整個請求進行保存&#xff0c;Ctrl S 2、這一次我們添加的是課程添加接口&#xff0c;以后一個接口完成&#xff0c;之后Ctrl S 就能夠保存 2.1 選擇方法 2.2 設置請求頭&#xff0c;參數數據后期我們通過配置設置就行 3、…

收銀一體化-億發2023智慧門店新零售營銷策略,實現全渠道運營

伴隨著互聯網電商行業的興起&#xff0c;以及用戶理念的改變&#xff0c;大量用戶從線下涌入線上&#xff0c;傳統的線下門店人流量急劇收縮&#xff0c;門店升級幾乎成為了每一個零售企業的發展之路。智慧門店新零售收銀解決方案是針對傳統零售企業面臨的諸多挑戰和問題&#…

Mathematica 與 Matlab 常見復雜指令集匯編

Mathematica 常見指令匯編 Mathematica 常見指令 NDSolve 求解結果的保存 sol NDSolve[{y[x] x^2, y[0] 0, g[x] -y[x]^2, g[0] 1}, {y, g}, {x, 0, 1}]; numericSoly sol[[1, 1, 2]]; numericSolg sol[[1, 2, 2]]; data Table[{x, numericSoly[x], numericSolg[x]},…

JVM——類加載器

回顧一下類加載過程 類加載過程&#xff1a;加載->連接->初始化。連接過程又可分為三步:驗證->準備->解析。 一個非數組類的加載階段&#xff08;加載階段獲取類的二進制字節流的動作&#xff09;是可控性最強的階段&#xff0c;這一步我們可以去完成還可以自定義…

【計算機網絡篇】UDP協議

?作者簡介&#xff1a;大家好&#xff0c;我是小楊 &#x1f4c3;個人主頁&#xff1a;「小楊」的csdn博客 &#x1f433;希望大家多多支持&#x1f970;一起進步呀&#xff01; UDP協議 1&#xff0c;UDP 簡介 UDP&#xff08;User Datagram Protocol&#xff09;是一種無連…

Flink學習筆記(一)

流處理 批處理應用于有界數據流的處理&#xff0c;流處理則應用于無界數據流的處理。 有界數據流&#xff1a;輸入數據有明確的開始和結束。 無界數據流&#xff1a;輸入數據沒有明確的開始和結束&#xff0c;或者說數據是無限的&#xff0c;數據通常會隨著時間變化而更新。 在…

Kaptcha的基本應用

Kaptcha Kaptcha 是一個用于生成和驗證驗證碼的 Java 庫&#xff0c;提供了豐富的生成和驗證功能&#xff0c;并支持自定義配置。它可以用于增加應用程序的安全性&#xff0c;防止機器人和惡意攻擊。 Kaptcha 可以生成各種類型的驗證碼&#xff0c;包括數字、字母、數字字母組…

KDD 2023 獲獎論文公布,港中文、港科大等獲最佳論文獎

ACM SIGKDD&#xff08;國際數據挖掘與知識發現大會&#xff0c;KDD&#xff09;是數據挖掘領域歷史最悠久、規模最大的國際頂級學術會議&#xff0c;也是首個引入大數據、數據科學、預測分析、眾包等概念的會議。 今年&#xff0c;第29屆 KDD 大會于上周在美國加州長灘圓滿結…

HTTP--Request詳解

請求消息數據格式 請求行 請求方式 請求url 請求協議/版本 GET /login.html HTTP/1.1 請求頭 客戶端瀏覽器告訴服務器一些信息 請求頭名稱: 請求頭值 常見的請求頭&#xff1a; User-Agent&#xff1a;瀏覽器告訴服務器&#xff0c;我訪問你使用的瀏覽器版本信息 可…

藍橋杯每日N題 (消滅老鼠)

大家好 我是寸鐵 希望這篇題解對你有用&#xff0c;麻煩動動手指點個贊或關注&#xff0c;感謝您的關注 不清楚藍橋杯考什么的點點下方&#x1f447; 考點秘籍 想背純享模版的伙伴們點點下方&#x1f447; 藍橋杯省一你一定不能錯過的模板大全(第一期) 藍橋杯省一你一定不…

【日常積累】HTTP和HTTPS的區別

背景 在運維面試中&#xff0c;經常會遇到面試官提問http和https的區別&#xff0c;今天咱們先來簡單了解一下。 超文本傳輸協議HTTP被用于在Web瀏覽器和網站服務器之間傳遞信息&#xff0c;HTTP協議以明文方式發送內容&#xff0c;不提供任何方式的數據加密&#xff0c;如果…

09- DMA(DirectMemoryAccess直接存儲器訪問)

DMA 09 、DMA(DirectMemoryAccess直接存儲器訪問)DMA配置流程 09 、DMA(DirectMemoryAccess直接存儲器訪問) DMA配置流程 dma.c文件 main.c文件 詳見《stm32中文參考手冊》表57。

tsconfig.json和jsconfig.json配置

{// 編譯選項"compilerOptions": {// 生成代碼的語言版本&#xff1a;將我們寫的 TS 代碼編譯成哪個版本的 JS 代碼// 命令行&#xff1a; tsc --target es5 11-測試TS配置文件.ts"target": "es5",// 指定要包含在編譯中的 library"lib&quo…

3年 Android 開發的面試心經(后悔當初沒有拿 N+1)

作者&#xff1a;勇闖天涯 當某人順利通過大廠面試時&#xff0c;總會有人認為這是運氣比較好罷了&#xff0c;但他們不曾得知對方之前受過多少苦和委屈&#xff0c;又付出了多少努力一步步去突破這些困境。正是因為他們的努力付出&#xff0c;在合適的時間與地點&#xff0c;用…

SSH連接工具匯總

xshell 這是個熟悉的軟件啦&#xff0c;目前我正在使用Xshell_7 鏈接&#xff1a;https://www.xshell.com/zh/xshell/ FinalShell 國產軟件&#xff0c;有windows和MAC版本&#xff1b;使用方便而且免費&#xff0c;但是軟件比較占用內存。但是都2021年了&#xff0c;筆記本…

AlphaZero能否從圍棋和國際象棋飛躍到量子計算?

一項新的研究表明&#xff0c;DeepMind驚人的游戲算法AlphaZero可以幫助釋放量子計算的力量和潛力。 自兩年多前出現以來&#xff0c;AlphaZero一再證明了其快速學習能力&#xff0c;將自己提升到圍棋&#xff0c;國際象棋和將棋&#xff08;日本象棋&#xff09;的特級大師級別…

VHDL記錄

文章目錄 使用function名稱作為“常量”numeric_std包集中使用乘法的注意項variable的使用對于entity設置屬性的方法在entity聲明中嵌入function的定義VHDL仿真讀寫文件File declaration/File handingFile readingFile writing小例子 使用函數 模塊中打印出調試信息 使用functi…

RTC實驗

一、RTC簡介 RTC(Real Time Clock)即實時時鐘&#xff0c;它是一個可以為系統提供精確的時間基準的元器件&#xff0c;RTC一般采用精度較高的晶振作為時鐘源&#xff0c;有些RTC為了在主電源掉電時還可以工作&#xff0c;需要外加電池供電BCD碼&#xff0c;四位二進制表示一位…