理解为运行在SSL(Secure Sockets Layer)或TLS(Transport Layer Security)协议所构建的安全层之上的HTTP协议
在使用https 之前,需要自己建立一个证书, 以便测试使用。正式使用需要去权威的CA机构申请证书。
这个时候需要用到openssl 但是低版本的是有漏洞的,需要升级一下。
openssl是一个开源程序的套件、这个套件有三个部分组成:
其他系统使用连接中的方式下载升级, 因为我用的mac, 升级过程碰到一些问题,所以这里记一下。
OSX 有一个系统完整性保护,所以没权限删除系统自带的openssl, 但是我们不需要删除,只需要做软连接就可以解决
$PATH
值echo $PATH //打印环境变量
/usr/local/bin:<....>:/usr/bin:/bin
设置/usr/local/bin
在 /usr/bin
之前,这样保证我们设置到/usr/local/bin
先起作用。
如果不是这样,就需要设置$PATH
值
echo $SHELL //确认自己使用的shell
/bin/bash //修改 ~/.bash_profile
或
/bin/zsh //修改 ~/.zshrc
brew install openssl
brew link openssl
//如果失败 加参数 --farce
brew link openssl --farce
openssl version -a
//生成服务器端的私钥, 要使用2048位生成,1024位已经不安全了, 看上面的链接
openssl genrsa -out server.key 2048
//生成服务器端证书
openssl req -new -x509 -key server.key -out server.pem -days 365
//生成客户端的私钥
openssl genrsa -out client.key 2048
//生成客户端的证书
openssl req -new -x509 -key client.key -out client.pem -days 365
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,
"Hi, This is an example of https service in golang!")
}
func main() {
http.HandleFunc("/", handler)
//server.crt 服务端证书地址
//server.key 服务端私钥地址
http.ListenAndServeTLS(":8081", "server.crt", "server.key", nil)
}
让这个例子能先Run起来,我们在程序所在目录先执行下面命令,利用openssl生成server.crt和server.key文件,供程序使用.
openssl genrsa -out server.key 2048
openssl req -new -x509 -key server.key -out server.crt -days 365
显示为
$openssl genrsa -out server.key 2048
Generating RSA private key, 2048 bit long modulus
…………….+++
……………+++
e is 65537 (0×10001)
$openssl req -new -x509 -key server.key -out server.crt -days 365
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
—–
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:localhost //这里注意输入
Email Address []:
然后 go run test.go
运行
在浏览器中输入 https://localhost:8081 , 忽略继续后,返回结果
或者使用 curl -k https://localhost:8081
测试
注意如果不加-k,curl会报如下错误:
$curl https://localhost:8081
curl: (60) SSL certificate problem: Invalid certificate chain
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the –cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or –insecure) option.
package main
import (
"crypto/tls"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //跳过验证
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://localhost:8081")
if err != nil {
fmt.Println("error:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
服务端代码:
首先从上面我们创建的服务器私钥和pem文件中得到证书cert,并且生成一个tls.Config对象。这个对象有多个字段可以设置,本例中我们使用它的默认值。 然后用tls.Listen开始监听客户端的连接,accept后得到一个net.Conn,后续处理和普通的TCP程序一样。
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
}
}
}
客户端代码:
InsecureSkipVerify用来控制客户端是否证书和服务器主机名。如果设置为true,则不会校验证书以及证书中的主机名和服务器主机名是否一致。 因为在我们的例子中使用自签名的证书,所以设置它为true,仅仅用于测试目的。
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]))
}
客户端:
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]))
}
因为需要验证客户端,我们需要额外配置下面两个字段
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: clientCertPool,
然后客户端也配置这个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]))
}
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有