grpc使用自制CA证书校验公网上的连接请求

方法在最后一小节,前面都是用到的知识的总结,了解的可以跳过。


1. 对称加密和非对称加密

对称加密:加密和解密都用同一个密码
非对称加密:公钥对所有人公开,发送者加密用公钥,接收者唯一掌握私钥,对公钥加密内容的解密用且只能用私钥
非对称加密缺点:加密速度比对称慢
非对称加密优点:公钥本身就是公开给别人的,所以不用担心被窃取,私钥永远在自己手里,只有自己能解密消息

2. https和http

  https在http的基础上,对传输内容进行了加密。
  我们都知道http建立连接的三次握手和四次挥手过程。如果使用的是https,在三次握手过后,需要进行SSL握手,然后客户端和服务端通信过程中使用SSL(新版本叫TLS了)的互相加解密。通信完之后,在四次挥手之前,先关闭SSL连接。
  从这里能看出,https比http要慢(多了SSL握手、挥手,就多了好几次TCP通信,建立连接之后每次通信还要加解密,http/1.1开始支持长连接,以及http/2支持多路复用,都可以减少建立连接的次数,也就减少了多次http请求下每次http请求的平均耗时)。
  https相比http还可以防篡改。接收到消息之后计算出数字签名,对比收到的数字签名,就知道消息体是否被篡改了。(想了解数字签名可以参考阿里云视频监控产品OpenAPI的签名机制

3. https单向认证和双向认证

图来自这里

https单向认证

https双向认证

  从上面对比可以看到,https使用的非对称加密是用在客户端和服务端交互SSL信息的过程中,SSL握手完成后,客户端和服务端通信使用的还是对称加密,对称加密的密钥在本次连接随机生成,其他的连接以及本次连接关闭后都不能使用。
  双向认证和单向认证的区别就在于,客户端需要向服务端发送自己的证书,服务端需要校验客户端的证书,以及服务端选择加密方案通知客户端时也是加密的。

4. CA证书和自制证书

  CA(Certificate Authority)是负责管理和签发证书的第三方权威机构,常见的有Symantec、GeoTrust、Comodo等等,他们是所有行业和公众都信任的、认可的,并负责审核向他们申请证书的网站的安全性。
  CA证书,就是CA颁发的证书,可用于验证网站是否可信(针对HTTPS)、验证某文件是否可信(是否被篡改)等,也可以用一个证书来证明另一个证书是真实可信,最顶级的证书称为根证书。除了根证书(自己证明自己是可靠),其它证书都要依靠上一级的证书,来证明自己。
  我们用的操作系统都预置了很多可信任的根证书,SSL握手时服务器会把它的服务器证书发给浏览器。例如CSDN的服务器证书是GeoTrust颁发的,本地的GeoTrust根证书可以证明CSDN的服务器证书是真的,值得信任,于是我们访问CSDN时浏览器就建立了和CSDN服务器的https连接。
  我们也可以自己做CA根证书,我们自己的机器或者访问我们服务的客户的机器,都安装上该CA根证书。12306以前就是这样干的(SRCA就是12306的根证书,现在已经换成DigiCert的证书了)。
  关于CA证书更多的解释,以及各种证书文件的区别,参考这里
  自制CA证书可以用OpenSSL命令行工具,linux基本都自带,也可以使用GUI工具,参考这里

5. 利用chrome和nginx实践一下单向认证和双向认证

  先假定我们已经做好了证书文件,包括根证书ca.pem、服务端证书server.pem、服务端私钥server.key、客户端证书client.pem、客户端私钥client.key,可以来这里下载我做好的,可以用于你自己测试,提取码:yd11。
  nginx加上配置文件:

server {
    listen       443 ssl; # ssl代表该端口监听启用https
    server_name  localhost;
    ssl_certificate      /data/sslKey/server.pem;  # 证书
    ssl_certificate_key  /data/sslKey/server.key;  # 私钥
    ssl_client_certificate /data/sslKey/ca.pem;    # 根证书,用于验证各个下级证书
    ssl_verify_client on; # 校验客户端证书开启
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

其中:

ssl_certificate      /data/sslKey/server.pem;  # server证书公钥
ssl_certificate_key  /data/sslKey/server.key;  # server私钥

很常见。
  通常我们需要让自己的网站变成https访问时都是这么做。使用nginx对外暴露https请求接口,nginx到后端的内网服务仍然是http,改动小,效率高。
  只有这两项配置就是单向认证,即客户端需要校验服务端的证书来确信服务端是安全网站。这时候访问https://localhost:443会提示不安全的网站(以下所有截图都是chrome浏览器的交互,其他浏览器可能不是这样,自行处理),因为服务端提供的证书是我们自制的,操作系统预存的证书无法识别该证书。

不安全的网站

  点击继续访问代表我们手动告诉浏览器信任这个网站,就可以继续访问了。
继续访问

再加上:

ssl_client_certificate /data/sslKey/ca.pem;    # 根证书,用于验证各个下级证书
ssl_verify_client on; # 校验客户端证书开启

这两项配置就是双向认证。
  服务端需要验证客户端的证书,我们直接访问就会得到报错400 No Required SSL certificate was sent,需要在chrome中导入自制的client.pem证书,导入方法参考这里
  需要注意的是windows导入证书的格式不是pem和crt,需要转换一下才能导入,OpenSSL命令行工具可以转换,上面的GUI工具也可以在导出时选择指定格式。
  再次访问chrome会弹窗提示,

选择证书

  选择刚才导入的证书就能正常访问了。
nginx访问结果

6. 利用自制CA证书双向认证实现安全访问

  以go代码示例,仍然使用上面的证书文件,加载证书文件的代码:

import (
    "crypto/tls"
    "crypto/x509"
    "io/ioutil"
    "log"
    "google.golang.org/grpc/credentials"
)

// GetServerCredentials 服务端证书
func GetServerCredentials() credentials.TransportCredentials {
    cert, err := tls.LoadX509KeyPair("cert/server.pem", "cert/server.key")
    if err != nil {
        log.Fatalf("加载服务端证书失败, err: %v\n", err)
    }
    certPool := x509.NewCertPool()
    ca, err := ioutil.ReadFile("cert/ca.pem")
    if err != nil {
        log.Fatalf("读取公钥文件失败: %v\n", err)
    }
    certPool.AppendCertsFromPEM(ca)
    creds := credentials.NewTLS(&tls.Config{
        Certificates: []tls.Certificate{cert},
        ClientAuth:   tls.RequireAndVerifyClientCert,
        ClientCAs:    certPool,
    })
    return creds
}

// GetClientCredentials 客户端证书
func GetClientCredentials() credentials.TransportCredentials {
    cert, err := tls.LoadX509KeyPair("cert/client.pem", "cert/client.key")
    if err != nil {
        log.Fatalf("加载客户端证书失败, err: %v\n", err)
    }
    certPool := x509.NewCertPool()
    ca, err := ioutil.ReadFile("cert/ca.pem")
    if err != nil {
        log.Fatalf("读取公钥文件失败: %v\n", err)
    }
    certPool.AppendCertsFromPEM(ca)
    creds := credentials.NewTLS(&tls.Config{
        Certificates: []tls.Certificate{cert},
        ServerName:   "localhost",
        RootCAs:      certPool,
    })
    return creds
}

  服务端加上:

opts := []grpc.ServerOption{
    grpc.Creds(utils.GetServerCredentials()),
}

  客户端加上:

grpc.Dial(
  "localhost:8080",
    grpc.WithTransportCredentials(cert.GetClientCredentials()),

  如果服务端使用了证书,客户端没有使用证书,在grpc.Dial()时连接可以建立成功,但是访问时会报错:

rpc error: code = Unavailable desc = connection closed

并且可以一直访问一直报错。
  这里涉及到gRPC连接机制的问题。调用Dial或者DialContext函数创建连接时,默认只是返回ClientConn结构体指针,同时会启动一个Goroutine异步的去建立连接,连接失败会一直重试,这个机制可以避免服务器因为连接空闲时间过长关闭连接、服务器重启等造成的客户端连接失效问题,可以完美的解决连接的超时与保活问题。
  如果想要等连接建立完再返回(起到创建连接时就检测连接是否可用的目的),可以指定grpc.WithBlock()。连接不上就会报错:

context deadline exceeded

  如果服务端没有使用证书,客户端使用了,也是可以成功建立连接,但是访问时报错:

connection error: desc = "transport: authentication handshake failed: tls: first record does not look like a TLS handshake"

  因为客户端收不到服务端的TLS握手信息(服务端不使用证书,根本就不知道要TLS握手)。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270