笔记:OpenSSL 生成「自签名」证书遇到的 missing_subjectAltName 问题


 拍摄于 2017-05-01。
拍摄于 2017-05-01。

通过 OpenSSL 配置运行本地 HTTPS(TLS/SSL)服务的步骤

在服务器上配置配置 HTTPS 的过程并最基本的就是以下三个步骤,每一个步骤中都会生成一个新的文件:

  1. 生成强加密的私钥 -> .key 文件;
  2. 创建证书签名申请,并且发送给 CA -> .csr(certificate signing request)文件;
  3. 在 Web 服务器上安装 CA 提供的证书 -> .crt(certificate)文件。

在生产环境的 HTTPS 配置中,第二步生成的 .csr 文件需要和第一步产生的 .key 文件一起交给颁布证书 CA,审核通过后得到正式的证书 .crt 文件,再到第三步将其部署到自己的服务器。

而在到本地 HTTPS 环境的搭建,就不存在发送给 CA 进行审核,取而代之直接在本地进行「自签名」的方式进行认证。

步骤对应的操作

关于 HTTPS 本身原理,握手,非对称加密之类的概念,笔记不会做很多展开。相关可以翻下《HTTP 权威指南》和《HTTPS 权威指南》,前者在《第十四章-安全 HTTP》对涉及到的概念都做了解释,而后者除了开始部分关于概念的叙述,后半部分有很多在各平台、运行环境的实操实例。

第一步,生成强加密的私钥

  1. 首先,最后部分传入的 4096 意思是包含的比特长度。
  2. 最基本生成私有密钥的命令如上,不过还可以针对秘钥的生成指定加密方式,比如 AES128,AES256,3DES 就对应 -aes128-aes256-des3 这三个 flag;
  3. 如果对 key 本身进行了加密,之后无论是借由 key 生成 .csr 文件、对 key 的内容进行读取展示、进行「自签名」或者提交给我 CA 认证的时候,每次都会被提示需要输入密码。同时如果用来加密私有密钥的密码丢失,意味着通过这个密钥生成获取到的证书失效;
  4. 关于对私有秘钥加密方式的问题可以参考:DES is Not SecureWhy is AES more secure than DES?
  5. 查看私有密钥内容:如果直接使用 cat 命令进行查看,私有密钥文件中只是一串很长的随机数,随机数的前后是 BEGIN RSA PRIVATE KEY 和 END RSA PRIVATE KEY, 可实际上这串随机数是有实际意义的,需要通过 openssl rsa -text -in name-of-private-key.key 命令获取,内容中牵扯到一些密码学方面的知识,这里无能展开。

第二步,通过私有秘钥生成 .csr

一旦有了私钥,就可以创建证书签名申请(certificate signing request,简称 CSR)。这是要求 Catificate Authority 给证书签名的一种正式申请。以第一步中生成的私有密钥为例,生成对应 .csr 文件的命令如下:

在命令行中输入以上命令,会提示填写一些信息,申请人所在国家,地区,网站本身的域名等,如下:

  • 在这里虽然是通过私有密钥来生成 .csr ,但是 .csr 文件中不包含任何私有密钥的信息,只包含了公钥和一些认证实体信息(命令行中 prompt 提示填写的那些内容);
  • 具体填入的信息,除了 Common Name 这个 field 因为是本地环境需要输入 localhost 之外,别的基本都随意;
  • 命令行 prompt 方式只是生成 .csr 的一种方式,另外还有通过 .cnf 配置文件配合命令生成的方式,下文会提到;

第三步,通过密钥和 .csr 生成「自签名」证书


三步完成,如果将所有生成的文件都以 ssl 来作为命名,那么现在文件夹里就有三个文件:ssl.keyssl.csrssl.crt;和 .key 文件类似,如果直接使用 cat 命令来查看其内容也会完全无效,可以是用如下命令对其内容进行打印:

ssl cert v1
ssl cert v1

遇到的问题:missing_subjectAltName

此时浏览器,比如 Chrome,https://localhost 仍不会被认为是一条 secure 的链接,具体原因可以点击「Advanced」按钮,截图:

missing_subjectAltName
报错 missing_subjectAltName

具体的报错内容是:

This server could not prove that it is localhost; its security certificate is from [missing_subjectAltName]. This may be caused by a misconfiguration or an attacker intercepting your connection.

关于 SubjectAltName 可以查看相关 文档。大概可以理解为:

在生成 .csr 的时候填入的很多信息中,包含了一个叫做 Common Name(CN)的 field,以前这个 CN 可以直接填写 server name 或者 IP,但之后规定了需要使用 Subject Alternative Name(SAN) 来指定 server name, IP1

同时需要指出, 文档 中最后一部分「Verify the Signed (Public) Keyfile with OpenSSL」生成的范例证书,仔细观察,对比上文生成 certs 内容,会发现,范例中的 version 是 3,而上文中我们生成的证书是 1,其对应的是 x509 证书的版本。

解决问题

通过已有证书添加 v3.ext 重新生成证书

对于生成 v3 版本证书,可以通过单独添加一个 v3.ext 的方法对当前证书进行重新生成,v3.ext 的内容包含了报错中的 SubjectAltName2

最后的 DNS.1 = localhost 也可以直接写成 DNS = localhost

运行命令:

通过 .cnf 配置文件生成证书

在第二步中提到,除了通过命令行 prompt 的方式生成 .csr 文件,还可以通过一个 .cnf 方式对 .key、.csr、.crt 文件进行生成。

[req] 部分的最后,定义了 x509_extensions = v3_req,之后在 v3_req 中又定义了 subjectAltName = [alt_names],通过以下这条命令可以配合 .cnf 文件生成本地「自签名」的 .key 和 .crt。

正确生成的证书以及验证

通过上一步生成的证书,使用 openssl x509 -in [name].crt -text -noout 命令对其内容进行查看,在证书的开头可以清楚看到 Version: 3 (0x2),对应下面截图的开始部分。完整生成的证书贴在了 GistversionsubjectAltName 都用 ^^^^ 做了标记,证书本身很长非常占用篇幅,这里就不全部贴出,自行查看。

ssl certs v3
ssl certs v3.

参考附录中的在 macOS 下对「自签名」证书授权信任为系统添加进 macOS 系统中。如果按照上文前三步经历了和我一样的试错过程,切记把前三部已经生成的那张 localhost 的证书从 keychain 中删除

完成在 macOS 的 keychain.app 添加证书,重启 Node.js 的 server.js,之后在 Chrome 中打开 https://locahost,成功,截图如下。同时 Safari 也没问题,但如果使用 Firefox 访问 https://localhost,则依旧会报错不安全,因为 Firefox 对证书的认证没有走 macOS 系统内而有一套自己的认证机制,解决的方式也很简单,通过 Firefox 报错页面下的「add expection」按钮添加特例就可以。

附录

例:macOS 本地为 Apache VirtualHost 生成可用的 TLS/SSL 证书

以本地 Apache 新建两个分别为 example.comexample_another.com 的 VirtualHost 为例3,Apache 的 http-vhost.conf 里默认如下4

http://example.com:80 的预览,http://example_another.com:80 类似,不贴了

这里直接本地通过 .conf 的方式生成证书,保存为 ssl.conf,内容如下。注意 CN(Common Name)的内容实际上和域名本身没有关系,这里写的是 Localhost's VirtualHost,而真正和相关域名有直接关系的是 subjectAltName

接着通过 openssl 相关命令得到需要的 .key.crt 文件,分别保存为 ssl.keyssl.crt :

告诉 macOS 「始终信任」刚才生成的 ssl.crt :

打开 macOS 的 KeyChain.app 可以看到名称为 ,双击后拖到底部可以看到 altername 中的 DNS 设置已生效:

更新 http-vhost.conf,添加上 SSL 相关内容:

最后通过 sudo apachectl -k restart 重启 Apache 就可以使修改生效:

https://example.com:443 预览,https://example_another.com:443 也生效了,截图就不放了太占版面。

在 macOS 下对「自签名」证书授权信任

自签名证书生成完毕并不是所有的工作就结束了,还需要将 .crt 文件提交给系统授以信任,以便浏览器在访问本地站点的时候可以从系统提供的信息中得出当前证书可信的结果。

在终端里输入以下命令就可以(修改最后一行 .crt 路径)


Node.js 启用 HTTPS 服务的代码片段

在 Nodejs 中开启本地 HTTPS 服务最基本的代码,需要引入生成的 [name].key 和 [name].crt 文件。而具体的开启命令,由于是 443 端口,所以需要 sudo 权限(作为笔记例子的一部分,本地测试这么写不会带来很大问题,但别的情况基本不这么干),在浏览器中打开 https://localhost


Python 启用 HTTPS 服务的代码片段

做测试的时候跑本地的服务,一直更习惯使用 Python 的相关命令,这里贴一下 Python 跑 HTTP 和 HTTPS 的最基本方法:

HTTP

HTTPS

截图

参考


  1. 更多的参考可以看这里<How to make self-signed certificate for localhost?》及其提到的 《RFC 5280》。 ↩︎

  2. Fixing Chrome 58+ [missing_subjectAltName] with openssl when using self signed certificates》。 ↩︎

  3. 这里忽略了修改 /private/etc/hosts 的操作,需要将两个 vhost 指向本地。 ↩︎

  4. example_anther.com 作为 vhost,其中的下划线会导致问题,参考这里《Bad Request Your browser sent a request that this server could not understand》,大概的意思是说 vhost 中如果使用下划线并不是一种安全的做法,如果一定需要,可以在 httpd.conf 中加入一句 HttpProtocolOptions unsafe。 ↩︎

感谢阅读

你们好, 2018 年初把小站从 Jekyll 迁移到 Hugo 的过程中,删除了评论区放的 Disqus 插件,考虑有二:首先无论评论、还是对笔记内容的进一步讨论,读者们更喜欢通过邮件、或者 Twitter 私信的方式来沟通;其次一年多以来 Disqus 后台能看到几乎都是垃圾留言(spam),所以这里直接贴一下邮件、以及 Twitter 账户 地址。

技术发展迭代很快,所以这些笔记内容也有类似新闻的时效性,不免有过时、或者错误的地方,欢迎指正 ^_^。

BEST
Lien(A.K.A 胡椒)
本站总访问量 本站总访客量 本文总阅读量