只有xx.key和xx.pem时,https客户端如何避免中间人攻击,确保证书没问题?

jan-bar · 2022-10-02 12:53:52 · 2517 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2022-10-02 12:53:52 的主题,其中的信息可能已经有所发展或是发生改变。

我通过阿里云申请的证书里面只有xx.pemxx.key这两个文件,我查找了很多证书相关的资料,客户端确认服务器证书貌似需要ca.crt这种CA证书,我没有这个。然后我查了下中间人攻击就是有人替换了证书让客户端以为证书没问题,但实际用的是中间人给的证书。

所以我尝试如下代码,不知道可不可以防止中间人攻击,我的客户端直接读取xx.pem文件和go库连接https服务器获取的证书内容进行比较,如果不一样则说明被篡改,终止当前连接。我不知道我这样做对不对,有没有大佬确认一下,或者有更好方案啊?

原始需求就是当只有xx.pemxx.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
}

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2517 次点击  
加入收藏 微博
6 回复  |  直到 2022-10-12 09:44:08
lysShub
lysShub · #1 · 3年之前

xx.key就是私钥,pem/crt是公钥、证书,只是格式不同而已

jan-bar
jan-bar · #2 · 3年之前
lysShublysShub #1 回复

xx.key就是私钥,pem/crt是公钥、证书,只是格式不同而已

意思是我客户端把pem填到RootCA字段完成校验就可以了么?

Felixw
Felixw · #3 · 3年之前

虽然你申请得到的只有只有xx.pem和xx.key,但是并不代表没有ca证书。这里的ca是具有公信力的证书机构授权的根证书,已经被你的电脑或者手机默认安装,所以客户端直接相信,也就不需要你客户端去填写根证书。 而自建的证书,才需要客户端手动填写,或者手动选择相信,因为主机上没有找到你的根证书,压根不相信它。

jan-bar
jan-bar · #4 · 3年之前
FelixwFelixw #3 回复

虽然你申请得到的只有只有xx.pem和xx.key,但是并不代表没有ca证书。这里的ca是具有公信力的证书机构授权的根证书,已经被你的电脑或者手机默认安装,所以客户端直接相信,也就不需要你客户端去填写根证书。 而自建的证书,才需要客户端手动填写,或者手动选择相信,因为主机上没有找到你的根证书,压根不相信它。

这个逻辑我清楚,我的问题是中间人也搞一个受信任的证书,那我的客户端如果只从本机预存的ca证书验证中间人的证书也能通过,那就可能造成中间人攻击吧?

还有就是有些公司发的电脑会悄悄装公司自己搞的ca证书在电脑上,这时候不知情的员工不就等于裸奔吗?

我是想知道我上面那种客户端代码里面直接校验从服务器获取的xx.pem和我使用的xx.pem进行比较,这样是否就可以完全避免中间人攻击了?毕竟我的客户端只认我用的那个xx.pem

pollyduan
pollyduan · #5 · 3年之前

中间人并没有这么容易,你访问的是你自己的域名,他没有你的域名归属,没办法用你的域名去申请证书吧。

jan-bar
jan-bar · #6 · 3年之前
pollyduanpollyduan #5 回复

中间人并没有这么容易,你访问的是你自己的域名,他没有你的域名归属,没办法用你的域名去申请证书吧。

但是我访问域名,中间会过很多路由器和一些网络设备,客户端只验证证书域名不太靠谱,中间人可以在这些网络设备上伪造域名解析到他们的IP服务器上。甚至他们也可以伪造一个相同IP,让客户端以为访问到正确的IP上。但是证书却是成对出现,服务器上有私钥,所以我想通过上面代码,在客户端检查获取的pem证书和服务器一模一样,这样才信任这次连接,我只是想知道这种方式靠不靠谱额。

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传