使用Go實(shí)現(xiàn)TLS服務(wù)器和客戶端的示例
傳輸層安全協(xié)議(Transport Layer Security,縮寫:TLS),及其前身安全套接層(Secure Sockets Layer,縮寫:SSL)是一種安全協(xié)議,目的是為互聯(lián)網(wǎng)通信提供安全及數(shù)據(jù)完整性保障。
SSL包含記錄層(Record Layer)和傳輸層,記錄層協(xié)議確定了傳輸層數(shù)據(jù)的封裝格式。傳輸層安全協(xié)議使用X.509認(rèn)證,之后利用非對稱加密演算來對通信方做身份認(rèn)證,之后交換對稱密鑰作為會談密鑰(Session key)。這個(gè)會談密鑰是用來將通信兩方交換的數(shù)據(jù)做加密,保證兩個(gè)應(yīng)用間通信的保密性和可靠性,使客戶與服務(wù)器應(yīng)用之間的通信不被攻擊者竊聽。
本文并沒有提供一個(gè)TLS的深度教程,而是提供了兩個(gè)Go應(yīng)用TLS的簡單例子,用來演示使用Go語言快速開發(fā)安全網(wǎng)絡(luò)傳輸?shù)某绦颉?/p>
TLS歷史
- 1994年早期,NetScape公司設(shè)計(jì)了SSL協(xié)議(Secure Sockets Layer)的1.0版,但是未發(fā)布。
- 1994年11月,NetScape公司發(fā)布SSL 2.0版,很快發(fā)現(xiàn)有嚴(yán)重漏洞。
- 1996年11月,SSL 3.0版問世,得到大規(guī)模應(yīng)用。
- 1999年1月,互聯(lián)網(wǎng)標(biāo)準(zhǔn)化組織ISOC接替NetScape公司,發(fā)布了SSL的升級版TLS 1.0版。
- 2006年4月和2008年8月,TLS進(jìn)行了兩次升級,分別為TLS 1.1版和TLS 1.2版。最新的變動是2011年TLS 1.2的修訂版。
現(xiàn)在正在制定 tls 1.3。
證書生成
首先我們創(chuàng)建私鑰和證書。
服務(wù)器端的證書生成
使用了"服務(wù)端證書"可以確保服務(wù)器不是假冒的。
1、 生成服務(wù)器端的私鑰
openssl genrsa -out server.key 2048
2、 生成服務(wù)器端證書
openssl req -new -x509 -key server.key -out server.pem -days 3650
或者
go run $GOROOT/src/crypto/tls/generate_cert.go --host localhost
客戶端的證書生成
除了"服務(wù)端證書",在某些場合中還會涉及到"客戶端證書"。所謂的"客戶端證書"就是用來證明客戶端訪問者的身份。
比如在某些金融公司的內(nèi)網(wǎng),你的電腦上必須部署"客戶端證書",才能打開重要服務(wù)器的頁面。
我會在后面的例子中演示"客戶端證書"的使用。
3、 生成客戶端的私鑰
openssl genrsa -out client.key 2048
4、 生成客戶端的證書
openssl req -new -x509 -key client.key -out client.pem -days 3650
或者使用下面的腳本:
#!/bin/bash # call this script with an email address (valid or not). # like: # ./makecert.sh demo@random.com mkdir certs rm certs/* echo "make server cert" openssl req -new -nodes -x509 -out certs/server.pem -keyout certs/server.key -days 3650 -subj "/C=DE/ST=NRW/L=Earth/O=Random Company/OU=IT/CN=www.random.com/emailAddress=$1" echo "make client cert" openssl req -new -nodes -x509 -out certs/client.pem -keyout certs/client.key -days 3650 -subj "/C=DE/ST=NRW/L=Earth/O=Random Company/OU=IT/CN=www.random.com/emailAddress=$1"
Golang 例子
Go Package tls部分實(shí)現(xiàn)了 tls 1.2的功能,可以滿足我們?nèi)粘5膽?yīng)用。Package crypto/x509提供了證書管理的相關(guān)操作。
服務(wù)器證書的使用
本節(jié)代碼提供了服務(wù)器使用證書的例子。下面的代碼是服務(wù)器的例子:
package main
import (
"bufio"
"crypto/tls"
"log"
"net"
)
func main() {
cert, err := tls.LoadX509KeyPair("server.pem", "server.key")
if err != nil {
log.Println(err)
return
}
config := &tls.Config{Certificates: []tls.Certificate{cert}}
ln, err := tls.Listen("tcp", ":443", config)
if err != nil {
log.Println(err)
return
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
log.Println(err)
continue
}
go handleConn(conn)
}
}
func handleConn(conn net.Conn) {
defer conn.Close()
r := bufio.NewReader(conn)
for {
msg, err := r.ReadString('\n')
if err != nil {
log.Println(err)
return
}
println(msg)
n, err := conn.Write([]byte("world\n"))
if err != nil {
log.Println(n, err)
return
}
}
}
首先從上面我們創(chuàng)建的服務(wù)器私鑰和pem文件中得到證書cert,并且生成一個(gè)tls.Config對象。這個(gè)對象有多個(gè)字段可以設(shè)置,本例中我們使用它的默認(rèn)值。
然后用tls.Listen開始監(jiān)聽客戶端的連接,accept后得到一個(gè)net.Conn,后續(xù)處理和普通的TCP程序一樣。
然后,我們看看客戶端是如何實(shí)現(xiàn)的:
package main
import (
"crypto/tls"
"log"
)
func main() {
conf := &tls.Config{
InsecureSkipVerify: true,
}
conn, err := tls.Dial("tcp", "127.0.0.1:443", conf)
if err != nil {
log.Println(err)
return
}
defer conn.Close()
n, err := conn.Write([]byte("hello\n"))
if err != nil {
log.Println(n, err)
return
}
buf := make([]byte, 100)
n, err = conn.Read(buf)
if err != nil {
log.Println(n, err)
return
}
println(string(buf[:n]))
}
InsecureSkipVerify用來控制客戶端是否證書和服務(wù)器主機(jī)名。如果設(shè)置為true,則不會校驗(yàn)證書以及證書中的主機(jī)名和服務(wù)器主機(jī)名是否一致。
因?yàn)樵谖覀兊睦又惺褂米院灻淖C書,所以設(shè)置它為true,僅僅用于測試目的。
可以看到,整個(gè)的程序編寫和普通的TCP程序的編寫差不太多,只不過初始需要做一些TLS的配置。
你可以go run server.go和go run client.go測試這個(gè)例子。
客戶端證書的使用
在有的情況下,需要雙向認(rèn)證,服務(wù)器也需要驗(yàn)證客戶端的真實(shí)性。在這種情況下,我們需要服務(wù)器和客戶端進(jìn)行一點(diǎn)額外的配置。
服務(wù)器端:
package main
import (
"bufio"
"crypto/tls"
"crypto/x509"
"io/ioutil"
"log"
"net"
)
func main() {
cert, err := tls.LoadX509KeyPair("server.pem", "server.key")
if err != nil {
log.Println(err)
return
}
certBytes, err := ioutil.ReadFile("client.pem")
if err != nil {
panic("Unable to read cert.pem")
}
clientCertPool := x509.NewCertPool()
ok := clientCertPool.AppendCertsFromPEM(certBytes)
if !ok {
panic("failed to parse root certificate")
}
config := &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: clientCertPool,
}
ln, err := tls.Listen("tcp", ":443", config)
if err != nil {
log.Println(err)
return
}
defer ln.Close()
for {
conn, err := ln.Accept()
if err != nil {
log.Println(err)
continue
}
go handleConn(conn)
}
}
func handleConn(conn net.Conn) {
defer conn.Close()
r := bufio.NewReader(conn)
for {
msg, err := r.ReadString('\n')
if err != nil {
log.Println(err)
return
}
println(msg)
n, err := conn.Write([]byte("world\n"))
if err != nil {
log.Println(n, err)
return
}
}
}
因?yàn)樾枰?yàn)證客戶端,我們需要額外配置下面兩個(gè)字段:
ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: clientCertPool,
然后客戶端也配置這個(gè)clientCertPool:
package main
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"log"
)
func main() {
cert, err := tls.LoadX509KeyPair("client.pem", "client.key")
if err != nil {
log.Println(err)
return
}
certBytes, err := ioutil.ReadFile("client.pem")
if err != nil {
panic("Unable to read cert.pem")
}
clientCertPool := x509.NewCertPool()
ok := clientCertPool.AppendCertsFromPEM(certBytes)
if !ok {
panic("failed to parse root certificate")
}
conf := &tls.Config{
RootCAs: clientCertPool,
Certificates: []tls.Certificate{cert},
InsecureSkipVerify: true,
}
conn, err := tls.Dial("tcp", "127.0.0.1:443", conf)
if err != nil {
log.Println(err)
return
}
defer conn.Close()
n, err := conn.Write([]byte("hello\n"))
if err != nil {
log.Println(n, err)
return
}
buf := make([]byte, 100)
n, err = conn.Read(buf)
if err != nil {
log.Println(n, err)
return
}
println(string(buf[:n]))
}
運(yùn)行這兩個(gè)代碼go run server2.go和go run client2.go,可以看到兩者可以正常的通訊,如果用前面的客戶端go run client.go,不能正常通訊,因?yàn)榍懊娴目蛻舳瞬]有提供客戶端證書。
更正 使用自定義的CA的例子可以參考 https://github.com/golang/net/tree/master/http2/h2demo
Make CA: $ openssl genrsa -out rootCA.key 2048 $ openssl req -x509 -new -nodes -key rootCA.key -days 1024 -out rootCA.pem ... install that to Firefox Make cert: $ openssl genrsa -out server.key 2048 $ openssl req -new -key server.key -out server.csr $ openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 500
到此這篇關(guān)于使用Go實(shí)現(xiàn)TLS服務(wù)器和客戶端的示例的文章就介紹到這了,更多相關(guān)Go TLS服務(wù)器和客戶端內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言面向?qū)ο笾械亩鄳B(tài)你學(xué)會了嗎
面向?qū)ο笾械亩鄳B(tài)(Polymorphism)是指一個(gè)對象可以具有多種不同的形態(tài)或表現(xiàn)方式,本文將通過一些簡單的示例為大家講解一下多態(tài)的實(shí)現(xiàn),需要的可以參考下2023-07-07
Golang實(shí)現(xiàn)秒讀32GB大文件示例步驟
這篇文章主要為大家介紹了Golang實(shí)現(xiàn)秒讀32GB大文件的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
golang常用庫之gorilla/mux-http路由庫使用詳解
這篇文章主要介紹了golang常用庫之gorilla/mux-http路由庫使用,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10
golang如何使用指針靈活操作內(nèi)存及unsafe包原理解析
本文將深入探討unsafe包的功能和原理,同時(shí),我們學(xué)習(xí)某種東西,一方面是為了實(shí)踐運(yùn)用,另一方面則是出于功利性面試的目的,所以,本文還會為大家介紹unsafe?包的典型應(yīng)用以及高頻面試題,感興趣的朋友跟隨小編一起看看吧2024-07-07
Golang負(fù)載均衡和?;钤O(shè)計(jì)原理示例探究
這篇文章主要為大家介紹了Golang負(fù)載均衡和?;钤O(shè)計(jì)原理示例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
GoLang中的互斥鎖Mutex和讀寫鎖RWMutex使用教程
RWMutex是一個(gè)讀/寫互斥鎖,在某一時(shí)刻只能由任意數(shù)量的reader持有或者一個(gè)writer持有。也就是說,要么放行任意數(shù)量的reader,多個(gè)reader可以并行讀;要么放行一個(gè)writer,多個(gè)writer需要串行寫2023-01-01

