參考:http://www.reibang.com/p/f88f1f3b23a1
1.創(chuàng)建證書
1.創(chuàng)建根證書私鑰長(zhǎng)度為2048
openssl genrsa -out ca.key 2048
2.利用私鑰創(chuàng)建根證書按照提示一路輸入:
openssl req -new -x509 -days 36500 -key ca.key -out ca.crt
3.創(chuàng)建長(zhǎng)度為2048的SSL證書私匙
openssl genrsa -out server.key 2048
4.利用剛才的私匙建立SSL證書請(qǐng)求一路向下:
openssl req -new -key server.key -out server.csr
5.當(dāng)前文件夾下運(yùn)行如下命令創(chuàng)建所需目錄:
mkdir dir demoCA &&cd demoCA&&mkdir newcerts&&echo '10' > serial &&touch index.txt&&cd ..
6.用CA根證書簽署SSL自建證書:
openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key
7.查看證書
openssl x509 -in server.crt -noout -text
2.代碼編寫
服務(wù)端:
package main
import (
"crypto/rand"
"crypto/tls"
"fmt"
"log"
"net"
"time"
)
func HandleClientConnect(conn net.Conn) {
defer conn.Close()
fmt.Println("Receive Connect Request From ", conn.RemoteAddr().String())
buffer := make([]byte, 1024)
for {
len, err := conn.Read(buffer)
if err != nil {
log.Println(err.Error())
break
}
fmt.Printf("Receive Data: %s\n", string(buffer[:len]))
//發(fā)送給客戶端
_, err = conn.Write([]byte("服務(wù)器收到數(shù)據(jù):" + string(buffer[:len])))
if err != nil {
break
}
}
fmt.Println("Client " + conn.RemoteAddr().String() + " Connection Closed.....")
}
func main() {
crt, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
log.Fatalln(err.Error())
}
tlsConfig := &tls.Config{}
tlsConfig.Certificates = []tls.Certificate{crt}
// Time returns the current time as the number of seconds since the epoch.
// If Time is nil, TLS uses time.Now.
tlsConfig.Time = time.Now
// Rand provides the source of entropy for nonces and RSA blinding.
// If Rand is nil, TLS uses the cryptographic random reader in package
// crypto/rand.
// The Reader must be safe for use by multiple goroutines.
tlsConfig.Rand = rand.Reader
l, err := tls.Listen("tcp", ":8888", tlsConfig)
if err != nil {
log.Fatalln(err.Error())
}
for {
conn, err := l.Accept()
if err != nil {
fmt.Println(err.Error())
continue
} else {
go HandleClientConnect(conn)
}
}
}
客戶端:
package main
import (
"crypto/tls"
"fmt"
"io"
"time"
"log"
)
func main() {
conf := &tls.Config{
InsecureSkipVerify: true, //這里是跳過(guò)證書驗(yàn)證胖眷,因?yàn)樽C書簽發(fā)機(jī)構(gòu)的CA證書是不被認(rèn)證的
}
//注意這里要使用證書中包含的主機(jī)名稱
conn, err := tls.Dial("tcp", "127.0.0.1:8888", conf)
if err != nil {
log.Fatalln(err.Error())
}
defer conn.Close()
log.Println("Client Connect To ", conn.RemoteAddr())
status := conn.ConnectionState()
fmt.Printf("%#v\n", status)
buf := make([]byte, 1024)
ticker := time.NewTicker(1 * time.Millisecond * 500)
for {
select {
case <-ticker.C:
{
_, err = io.WriteString(conn, "hello")
if err != nil {
log.Fatalln(err.Error())
}
len, err := conn.Read(buf)
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println("Receive From Server:", string(buf[:len]))
}
}
}
}
}
上述客戶端配置了InsecureSkipVerify參數(shù),該參數(shù)表示客戶端跳過(guò)對(duì)證書鏈的校驗(yàn)拙毫,因?yàn)镃A證書是不是計(jì)算機(jī)內(nèi)置的爆阶。那如果我們希望完整的驗(yàn)證過(guò)程埃儿,那么我們可以把CA證書也發(fā)給客戶端截亦,客戶端代碼修改為
// gohttps/6-dual-verify-certs/client.go
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
pool := x509.NewCertPool()
caCertPath := "ca.crt"
caCrt, err := ioutil.ReadFile(caCertPath)
if err != nil {
fmt.Println("ReadFile err:", err)
return
}
pool.AppendCertsFromPEM(caCrt)
cliCrt, err := tls.LoadX509KeyPair("client.crt", "client.key")
if err != nil {
fmt.Println("Loadx509keypair err:", err)
return
}
tr := &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: pool,
Certificates: []tls.Certificate{cliCrt},
},
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://localhost:8081")
if err != nil {
fmt.Println("Get error:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}