如何使用自签名 CA 和证书来保护我的在公网上的内容

做为一个喜欢折腾的人,我的搞了不少东西放在本身的服务器上,可是为了方便,可以在世界各地随时随地的打开查看和使用,我将服务器暴露到了公网中,固然了有些在公有云上的原本就暴露出来的。html

那么这里就有一个问题,我如何保护个人信息只能我来查看呢?git

  • 最简单的方法就是经过 HTTP Basic Auth + HTTPS。记住必定要上 https,不然你的密码也是会泄漏的。为何说简单呢?由于只须要在 Nginx 或 Traefik 上配置下就能够了。可是这个方案有一个很是麻烦的问题,就是过一段时间以后就要输入用户名和密码。时间短了,到无所谓,时间一长就会以为很烦。
  • 构建一套 token 验证体系,不论是使用 oauth 也好仍是 jwt 也好,都是能够的。安全性也是能够保证的,并且设置好 token 的时间长度,也能保证避免频繁的输入密码。可是这有一个问题就是实现起来太过于复杂,都快遇上公司的一套系统了。并且还要有各类登陆页面,想一想都烦。
  • 与上面相似,不过验证方式使用 Two Auth,也就是基于时间的 6 位数。可是依旧比较复杂。

我想了许久,有没有一种不须要输入密码,就能够验证安全的呢?由于是我一我的使用的,因此我根本不须要多用户系统,也就是说验证方式只须要一个密码就能够了。这我忽然想起了以前在写 gRPC 的时候有一个双向验证的参数,也能够验证客户端能够不能够。当时以为只是他们基于 h2 改的协议,结果我一查发现这原来就包含在 https 里面,准确说是 SSL 规范里面。(怪本身当初上计算机网络的时候没好好学这部分,居然连这个都不知道)github

那么至此,思路就很清晰了,给个人全部我的服务都添加 https 客户端校验。只要个人证书够安全,个人页面就是安全的(反正都是我我的的东西,直接拿着 U 盘处处拷贝,手机 Pad 用数据线发送,我就不信这样谁还能盗走个人证书,傲娇脸)算法

关于 SSL 证书的一些知识

  • 生成证书咱们主要采用 openssl 具体的安装教程我就不讲解了,有兴趣的小伙伴自行查阅,主要有下面几个步骤:
    • openssl genrsa:生成 Private Key,用于生成请求文件使用,这里用 .key 后缀。
    • openssl req:依赖上面生成的 Key 去生成 CSR,也就是证书请求文件。使用 .csr 后缀。这期间要填写一些信息,前面的几个大写字母是缩写,后面在命令行使用的时候会用到。
      • C(Country) 国家
      • ST(State/Province) 州或者省
      • L(Locality) 地区,国内写区便可
      • O(Organization) 组织
      • OU(Organization) 组织单位
      • CN(Common Name) 通用名,这个是很是重要的,影响了证书的显示名称和 HTTPS 的域名。
    • openssl x509:根据 x509 规范,利用 CA 的证书和私钥将 CSR 文件加密成真正可使用到的证书。使用 .crt 后缀
  • SSL 证书必需要采用 sha-2 加密算法。2015 年 12 月 31 日前,CA 机构还会颁发 SHA-1 签名的证书,可是以后只会签发 SHA-2 签名的证书了。Chrome 也会对 SHA-1 签名的证书提示不安全。在 openssl 中用的是 -sha-256 参数。
  • CRTPEM 的关系,你们能够简单的认为 PEM 是将证书 base64 以后的文件,而 CRT 是既能 base64 也能 binary 的一种文件格式。可是一般 openssl 产出的是 base64 的文件,你能够经过 -outform 参数控制产出的类型。

CA 的生成

有了 CA 咱们才能去给其余的证书签名,生成 CA 的过程很简单api

建立根钥

这个秘钥很是重要,任何得到了这个秘钥的人在知道密码的状况下均可以生成证书。因此请当心保存浏览器

openssl genrsa -des3 -out root.key 4096
复制代码
  • -des3 标明了私钥的加密方式,也就是带有密码。建议添加密码保护,这样即便私钥被窃取了,依旧没法对其余证书签名。你也能够更换其余的加密方式,具体的请自行 help。
  • 4096 表示秘钥的长度。

建立自签名证书

由于是 CA 证书,因此无法让别人去签名,只能自签名。这里能够认为是生成 CSR 和签名两部合成一步走。安全

openssl req -x509 -sha256 -new -key root.key -sha256 -days 1024 -out root.crt
复制代码

服务端证书生成

生成证书私钥

openssl genrsa -out your-domain.com.key 2048
复制代码

和 CA 证书不一样,这个私钥通常不须要加密,长度也能够短一些。服务器

生成证书请求文件

openssl req -new -key your-domain.com.key -out your-domain.com.csr
复制代码

这期间要填入一些信息,注意 CN 的名字必定要是你的域名。网络

使用 CA 对 CSR 签名

在 Chrome 58 以前,Chrome 会根据 CN 来检查访问的域名是否是和证书的域名一致,可是在 Chrome 58 以后,改成使用 SAN(Subject Alternative Name) 而不是 CN 检查域名的一致性。dom

而 SAN 属于 x509 扩展里面的内容,因此咱们须要经过 -extfile 参数来指定存放扩展内容的文件。

因此咱们须要额外建立一个 your-domain.com.ext 文件用来保存 SAN 信息,经过指定多个 DNS 从而能够实现多域名证书。

subjectAltName = @alt_names

[alt_names]
DNS.1 = your-domain.com
DNS.2 = *.your-domain.com
DNS.3 = *.api.your-domain.com
复制代码

以此类推,若是域名较少,还能够用另一种简写方案。

subjectAltName = DNS: your-domain.com, DNS: *.your-domain.com
复制代码

关于语法的更多内容请查看官方文档。在有了 ext 文件以后就直接能够开始签名了。

openssl x509 -req -sha256 -in your-domain.com.csr -CA root.crt -CAkey root.key -CAcreateserial -out your-domain.com.crt -days 365 -extfile your-domain.com.ext
复制代码

CAcreateserial 这个参数是比较有意思的,意思是若是证书没有 serial number 就建立一个,由于咱们是签名,因此确定会建立一个。序列号在这里的做用就是惟一标识一个证书,当有两个证书的时候,只有给这两个证书签名的 CA 和序列号都同样的状况下,咱们才认为这两个证书是一致的。除了自定生成,还能够经过 -set_serial 手动指定一个序列号。

当使用 -CAcreateserial 参数以后,会自动建立一个和 CA 文件名相同的,可是后缀是 .srl 的文件。这里存储了上一次生成的序列号,每次调用的时候都会读取并 +1 。也就是说每一次生成的证书的序列号都比上一次的加了一。

如今,只须要将 your-domain.com.crtyour-domain.com.key 放到服务端就可使用了。别忘了将 CA 添加系统当中,要否则浏览器访问会出现问题。

客户端证书生成

服务端有了以后,就须要生成客户端的证书,步骤和服务端基本一致,可是不须要 SAN 信息了。

openssl genrsa -out client.key 2048
# 这里也能够采用非交互形式,方便制做成命令行工具
openssl req -new \
	-key client.key \
	-subj "/C=CN/ST=Zhejiang/O=X/CN=*.your-domain.com" \ # 这里的缩写就是文章一开始所说的那些缩写
	-out client.csr
openssl x509 -req -in client.csr -CA root.crt -CAkey root.key -out client.crt -days 365
复制代码

只不过客户端验证须要的是 PKCS#12 格式,这种格式是将证书和私钥打包在了一块儿。由于系统须要知道一个证书的私钥和公钥,而证书只包含公钥和签名,不包含私钥,因此须要这种格式的温江将私钥和公钥都包含进来。

openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12
复制代码

这期间会提示你输入密码,用于安装的时候使用。也就是说不是什么人均可以安装客户端证书的,要有密码才行,这无疑又增长了必定的安全性。固然了,我试过不输入密码,可是好像有点问题,有兴趣的同窗能够本身尝试下。

客户端校验证书的使用

这里以 Node.js 举例。使用 https 模块,在建立的时候和普通的建立方式基本一致,可是须要额外指定 requestCertca 参数来开启客户端校验。

https.createServer({
	key: fs.readFileSync('your-domain.com.key'),
	cert: fs.readFileSync('your-domain.com.crt'),
	requestCert: true,
	ca: [fs.readFileSync('root.crt')], // 校验客户端证书的 CA
}, (req, resp) => {
	// blahblah
})
复制代码

这样只要客户端没有安装合法的证书,那么整个请求就是失败的。并且根本不会进入请求处理的回调函数中,这也意味着显示的错误是浏览器的默认错误。那么这对用户来说其实不太友好。

那么咱们能够经过在参数中添加 rejectUnauthorized: false 来关闭这个功能,也就是说无论客户端证书校验是正确仍是失败,均可以进入正常的回调流程。此时咱们只须要经过 req.client.authorized 来判断这个请求是否经过了客户端证书的校验,能够给予用户更详尽的错误提示。

另外咱们还能够经过 resp.connection.getPeerCertificate() 获取客户端证书的信息,甚至能够根据不一样的信息选择给予不一样的用户权限。

这里有一个 DEMO: www.xgheaven.net.cn:3443,你们打开以后应该会看到一个证书校验失败的提示。这里要说下,我这里的 DEMO 没有使用自签名的服务端证书,只是使用了自签名的 CA 去检查客户端证书。由于用本身签名的服务端证书的话,浏览器会提示不安全,由于用户么有安装自签名的 CA。

能够点击下载客户端证书按钮,安装客户端证书。由于客户端证书是有密码保护的,请输入页面上提示的密码。

再次刷新,若是是 Mac 系统,会提示你要使用哪一个客户端证书登陆,此时就说明安装成功了。

点击确认,可能还要输入一个系统密码容许 Chrome 访问 Keychain,一劳永逸的话在输入密码以后选择 Always Allow,今后就不须要再输入密码了。

按照道理,你就能够看到这个页面了。

结语

有了这个功能,我就能够将个人全部内容全盘私有化并且还能直接暴露在公网中。配合以前毕设搞的微服务化,简直不要美滋滋。若是以前是使用帐号密码登陆的,也能够接入这个方案。就是将登陆页面替换成证书校验就能够了。

Refs

相关文章
相关标签/搜索