我通过阿里云申请的证书里面只有xx.pem
和xx.key
这两个文件,我查找了很多证书相关的资料,客户端确认服务器证书貌似需要ca.crt
这种CA证书,我没有这个。然后我查了下中间人攻击就是有人替换了证书让客户端以为证书没问题,但实际用的是中间人给的证书。
所以我尝试如下代码,不知道可不可以防止中间人攻击,我的客户端直接读取xx.pem
文件和go库连接https服务器获取的证书内容进行比较,如果不一样则说明被篡改,终止当前连接。我不知道我这样做对不对,有没有大佬确认一下,或者有更好方案啊?
原始需求就是当只有xx.pem
和xx.key
服务器证书文件时,如何保证客户端连接是没有问题的?
package main
import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io"
"net/http"
"os"
)
func main() {
err := getPem()
if err != nil {
panic(err)
}
err = httpGet()
if err != nil {
panic(err)
}
}
var (
pemRawCerts [][]byte // xx.pem读取的原始数据
errCheckPem = errors.New("check xx.pem error")
)
func getPem() error {
certPEMBlock, err := os.ReadFile(`xxx.pem`)
if err != nil {
return err
}
var certDERBlock *pem.Block
for {
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
if certDERBlock == nil {
break
}
if certDERBlock.Type == "CERTIFICATE" {
tmp := make([]byte, len(certDERBlock.Bytes))
copy(tmp, certDERBlock.Bytes)
pemRawCerts = append(pemRawCerts, tmp)
}
}
return nil
}
func httpGet() error {
c := http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
VerifyPeerCertificate: func(data [][]byte, verifiedChains [][]*x509.Certificate) error {
if len(data) == len(pemRawCerts) {
for i := len(data) - 1; i >= 0; i-- {
if !bytes.Equal(data[i], pemRawCerts[i]) {
return errCheckPem
}
}
// 只有当证书的每一项都正确才确定当前连接没问题
return nil
}
return errCheckPem
},
},
},
}
resp, err := c.Get("https://xxx.com")
if err != nil {
return err
}
defer resp.Body.Close()
n, err := io.Copy(io.Discard, resp.Body)
fmt.Println(n)
return err
}
有疑问加站长微信联系(非本文作者)

xx.key就是私钥,pem/crt是公钥、证书,只是格式不同而已
意思是我客户端把pem填到RootCA字段完成校验就可以了么?
虽然你申请得到的只有只有xx.pem和xx.key,但是并不代表没有ca证书。这里的ca是具有公信力的证书机构授权的根证书,已经被你的电脑或者手机默认安装,所以客户端直接相信,也就不需要你客户端去填写根证书。 而自建的证书,才需要客户端手动填写,或者手动选择相信,因为主机上没有找到你的根证书,压根不相信它。
这个逻辑我清楚,我的问题是中间人也搞一个受信任的证书,那我的客户端如果只从本机预存的ca证书验证中间人的证书也能通过,那就可能造成中间人攻击吧?
还有就是有些公司发的电脑会悄悄装公司自己搞的ca证书在电脑上,这时候不知情的员工不就等于裸奔吗?
我是想知道我上面那种客户端代码里面直接校验从服务器获取的
xx.pem
和我使用的xx.pem
进行比较,这样是否就可以完全避免中间人攻击了?毕竟我的客户端只认我用的那个xx.pem
。中间人并没有这么容易,你访问的是你自己的域名,他没有你的域名归属,没办法用你的域名去申请证书吧。
但是我访问域名,中间会过很多路由器和一些网络设备,客户端只验证证书域名不太靠谱,中间人可以在这些网络设备上伪造域名解析到他们的IP服务器上。甚至他们也可以伪造一个相同IP,让客户端以为访问到正确的IP上。但是证书却是成对出现,服务器上有私钥,所以我想通过上面代码,在客户端检查获取的pem证书和服务器一模一样,这样才信任这次连接,我只是想知道这种方式靠不靠谱额。