已发表的技术专栏(订阅即可观看所有专栏)
0 grpc-go、protobuf、multus-cni 技术专栏 总入口
4 grpc、oauth2、openssl、双向认证、单向认证等专栏文章目录
1、简单介绍单向认证 |
- 一般情况下,客户端需要验证服务器端的证书,服务器端不需要验证客户端的证书
- 服务器端需要加载服务器端的证书,用以向客户端发送自己的证书
- 客户端,主要分为两种情况
- 客户端必须验证服务器端的证书
- 可以通过写代码的方式来验证服务器端的证书
- 可以通过curl方式来验证服务器端的证书
- 可以通过Postman方式来验证服务器端的证书
- 客户端不用证服务器端的证书
- 客户端必须验证服务器端的证书
2、证书制作 |
单向认证时,服务器端的证书,可以使用自签证书,也可以使用非自签证书
2.1、方式一:使用自签证书,作为服务器端的证书 |
就是自己给自己颁发证书
openssl req -newkey rsa:2048 -nodes -keyout server.key -x509 -days 365 -out server.crt -subj "/C=CN/ST=beijing/L=beijing/O=baidu/OU=bigdata/CN=www.golang-study.com/emailAddress=123456789@qq.com"
查看生成的证书
openssl x509 -in server.crt -noout -text
2.2、方式二:使用非自签方式 |
2.2.1、制作根证书 |
一次性方式生成根证书
openssl req -newkey rsa:2048 -nodes -keyout ca.key -x509 -days 365 -out ca.crt -subj "/C=CN/ST=beijing/L=beijing/O=baidu/OU=bigdata/CN=www.golang.com/emailAddress=000000@qq.com"
2.2.2、制作服务器端证书 |
2.2.2.1、第一步:生成服务器端密钥和服务器端证书签名 (SAN类型,即多个域名生效) |
openssl req -newkey rsa:2048 -nodes -keyout server.key \
-subj "/C=CN/ST=beijing/L=beijing/O=baidu/OU=bigdata/CN=www.golang-server.com/emailAddress=123456789@qq.com" \
-reqexts SAN \
-config <(cat /etc/pki/tls/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:*.org.golang-server.com,DNS:www.golang-server.cn")) \
-out server.csr
查看证书签名里,是否有SAN请求信息
openssl req -noout -text -in server.csr
2.2.2.2、第二步:根据CA证书,来颁发服务器端证书 |
openssl x509 -req -days 365 \
-in server.csr -out server.crt \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-extensions SAN \
-extfile <(cat /etc/pki/tls/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:*.org.golang-server.com,DNS:www.golang-server.cn"))
查看生成的证书
openssl x509 -in server.crt -noout -text
3、服务器端 |
注意,本次测试是单向认证测试。
服务器端不需要验证客户端的证书,因此,服务器端不需要加载根证书。
只需要加载服务器端的证书即可。
(在服务器端,根证书是用来验证客户端证书是否有效的。)
package main
import (
"encoding/json"
"flag"
"fmt"
"github.com/gorilla/mux"
"log"
"net/http"
)
var (
port int
hostname string
serverKey string
serverCert string
)
func init() {
flag.IntVar(&port, "port", 8080, "监听端口")
flag.StringVar(&hostname, "hostname", "0.0.0.0", "服务地址")
flag.StringVar(&serverKey, "serverKey", "8-WithTransportCredentials/https/one-way-authentication/tls/cert/server.key", "服务器密钥")
flag.StringVar(&serverCert, "serverCert", "8-WithTransportCredentials/https/one-way-authentication/tls/cert/server.crt", "服务器证书")
}
func startHttpsServer(address string, serverCert string, serverKey string, router *mux.Router) {
s := &http.Server{
Addr: address,
Handler: router,
}
err := s.ListenAndServeTLS(serverCert, serverKey)
if err != nil {
log.Fatalln("ListenAndServeTLS err:", err)
}
}
func SayHelloOpenssl(w http.ResponseWriter, r *http.Request) {
log.Println("Entry SayHello")
res := map[string]string{"hello": "单向认证OK"}
b, err := json.Marshal(res)
if err == nil {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
w.Write(b)
}
log.Println("Exit SayHello")
}
func main() {
flag.Parse()
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/svc/hello-openssl", SayHelloOpenssl).Methods("GET")
var address = fmt.Sprintf("%s:%d", hostname, port)
fmt.Println("Server listen on", address)
startHttpsServer(address, serverCert, serverKey, router)
fmt.Println("Exit main")
}
在单向认证测试中,服务器端最大的不同,就是下面的语句:
其他地方,就是普通的http例子
4、客户端 |
4.1、客户端必须验证服务器端证书 |
- 如果服务器端一侧加载的是自签证书的话,客户端一侧,需要加载的是服务器的证书,用服务器端的证书来验证服务器端的证书,自己验证自己
- 如果服务器端一侧加载的是非自签证书的话,客户端一侧,需要加载根证书,或者颁发服务器端证书的证书
4.1.1、使用go语言编写客户端验证服务器端证书 |
由于非自签的服务器端证书,是SAN类型的。
即,添加了多个域名,为了测试多个域名,
客户端采用了测试组的方式进行域名测试
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
"testing"
)
var client *http.Client
func init() {
pool := x509.NewCertPool()
caCertPath := "***注意这里****改成自己的实际地址(有可能是ca的绝对地址)/cert/ca.crt"
caCrt, err := ioutil.ReadFile(caCertPath)
if err != nil {
fmt.Println("ReadFile err:", err)
return
}
pool.AppendCertsFromPEM(caCrt)
tr := &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: pool,
},
}
client = &http.Client{Transport: tr}
}
// 测试单向认证
func TestOneWayAuthentication(t *testing.T) {
type test struct { // 定义test结构体
url string
}
tests := map[string]test{ // 测试组
"test1": {url: "https://www.golang-server.cn:8080/svc/hello-openssl"},
"test2": {url: "https://www.golang-server.com:8080/svc/hello-openssl"},
"test3": {url: "https://abc.org.golang-server.com:8080/svc/hello-openssl"},
"test4": {url: "https://www.golang-server.cn:8080/svc/hello-openssl"},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) { // 使用t.Run()执行子测试
resp, err := client.Get(tc.url)
if err != nil {
fmt.Println("Get error:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
})
}
}
跟普通的客户端相比,需要做的额外操作:
- 创建认证池
- 读取根认证,或者其他证书(这些证书是用来验证服务器端证书的)
- 将证书加载到认证池里
在/etc/hosts里设置域名,进行测试
查看服务器端的证书里,支持哪些域名呢?
openssl x509 -in server.crt -noout -text
一次性测试所有测试用例
仅测试某个测试用例
4.1.2、使用curl方式验证服务器端证书 |
4.1.3、使用Postman方式验证服务器端证书 |
当前postman版本:(Mac环境下)
先测试一下,不添加根证书的情况:
添加ca.crt证书,再测试
如何添加ca.crt证书?
开始测试
4.2、客户端不用验证服务器端证书 |
4.2.1、代码方式不验证 |
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://www.golang-server.cn:8080/svc/hello-openssl")
if err != nil {
fmt.Println("Get error:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}
4.2.2、curl方式不验证 |
4.2.3、postman方式不验证 |
4.2.3.1、方式一:可以直接发起请求,出现下图时,点击Disable SSL Verification即可 |
4.2.3.2、方式二:发起请求前,先修改参数,禁止验证服务器端证书功能 |
4.2.3.3、此时,如果想恢复验证,如何处理? |
下一篇文章
https双向认证介绍
本文含有隐藏内容,请 开通VIP 后查看