1.创建证书
1.创建根证书私钥长度为2048
openssl genrsa -out ca.key 2048
2.利用私钥创建根证书按照提示一路输入:
openssl req -new -x509 -days 36500 -key ca.key -out ca.crt
3.创建长度为2048的SSL证书私匙
openssl genrsa -out server.key 2048
4.利用刚才的私匙建立SSL证书请求一路向下:
openssl req -new -key server.key -out server.csr
5.当前文件夹下运行如下命令创建所需目录:
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.代码编写
服务端:
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]))
//发送给客户端
_, err = conn.Write([]byte("服务器收到数据:" + 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, //这里是跳过证书验证,因为证书签发机构的CA证书是不被认证的
}
//注意这里要使用证书中包含的主机名称
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参数,该参数表示客户端跳过对证书链的校验,因为CA证书是不是计算机内置的。那如果我们希望完整的验证过程,那么我们可以把CA证书也发给客户端,客户端代码修改为
// 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))
}