Sunday, March 24, 2019

Create My Own CA to Issue a Certificate

在这个信息爆炸的时代,网络安全越来越被人们所重视。在浏览网页的时候,你提交的数据会不会被黑客截获?你正在浏览的页面是否就是你认为的页面?还是被黑客伪造后的页面?你下载的文件,是否就是发布者发布的原始文件?还是被黑客植入了病毒之后的版本?……

为了回答以上问题,并且能够让我们拥有更加安全的网络环境,越来越多的网站采用https来让浏览器对服务器进行验证,并且加密浏览器与服务器之间传输的信息。也就是说黑客可以截获信息,但是无法解密信息。

作为一个网站,想要使用https必须有一个经过Trusted CA(Certificate Authority)签名的证书,以及对应的private key. 以前想要申请一个Trusted CA 签名的证书是需要交费的,所以很多小网站或者创业初期的网站为了节约成本而没有采用https,由此导致了网络环境的恶化。

在2016年4月12日,由Internet Security Research Group (ISRG)建立了一个非盈利性的CA ------ Let’s Encrypted. 所有网站的提供者都可以在Let’s Encrypted 上免费为自己的网站申请Trusted CA签名的证书。尽管步骤有些复杂,而且有一些限制,但是能够在不提高成本的情况下提高网站的安全性,对于网站提供者的吸引力是巨大的。之后许多著名的企业也通过对Let’s Encrypted. 提供赞助的方式来进一步改善全球的网络环境。其中Google, Cisco 等等都是广大的赞助者之一。

对于一个开发人员,在网站开发过程中是没有必要申请Let’s Encrypted签名的证书的,首先是网站的开发周期不定,也许今天申请了证书,结果网站还没有上线证书就过期了;再者,一般来讲开发人员都是在本地电脑上进行开发,即使在Let’s Encrypted申请了证书,浏览器也会因为本地的机器名与证书中的域名不匹配而弹出警告。所以在开发过程中,我们更适合自己创建一个CA,为我们的网站创建一个临时的证书,尽管我们的CA并不是Trusted的(之后会讲到如何让它看起来像是Trusted),但是对于开发阶段来说已经足够了。

下面我们将创建自己的CA,并且用这个CA为正处于开发中的网站签署一个证书。在这里我们需要工具openssl. 在Linux 系统中openssl 已经预装好了。

所谓的CA实际上就是一个Self-Signed Certificate 加上对应的Private Key。首先我们来生成我们来生成Private Key (至于Public Key 是可以从Private Key中抽取出来的).

$ openssl genrsa -aes256 -out ca.key 4096

为了安全考虑,如果我们将Private Key保存在文件中,应该用密码将文件保护起来。上面命令中-aes256就是我们采用的加密算法,在执行过程中会要求我们设置密码(pass phrase),之后如果想要读取ca.key中的Private Key就需要用到这个密码。我们看一下这个文件的内容.

$ cat ca.key

会看到文件的内容是一个加密的Private Key

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,0988C6461A58983453D0B4F3041BDF9D
……
-----END RSA PRIVATE KEY-----

接下来我们要用Private Key 来生成一个Self-Signed Certificate.

$ openssl req -new -x509 -key ca.key -days 3650 -out ca.crt

控制台中会要求我们输入一些信息来拼凑成Distinguished Name,因为我们是自己使用,可以根据自己的喜好进行填写。所有信息输入完成之后,会在当前目录生成ca.crt文件。这个文件就是我们需要的Self-Signed Certificate. 可以通过命令来看一下这个Self-Signed Certificate 中的内容。

$ openssl x509 -in ca.crt -noout –text

其中可以看到Issuer(签署人)和Subject(证书主体)是相同的,这就表示这是一个Self-Signed Certificate.

还可以看到关于Public Key的信息,Public Key是从ca.key文件中抽取出来的。

同时我们也可以看到签名算法sha256WithRSAEncryption,即先通过sha256对Subject进行散列,之后用RSA算法,结合private key对签名进行加密。

接下来我们要利用刚刚创建的CA来为自己的网站签发证书。

假设本地的IP地址是:11.11.11.11

首先和创建CA一样,我们也需要生成Private Key

$ openssl genrsa -aes256 -out 11.11.11.11.key 4096

接下来需要用Private Key 创建一个CSR (Certificate Sign Request),在CSR中会包含我们的主体信息,以及从Private Key中导出的Public Key。

$ openssl req -new -key 11.11.11.11.key -out 11.11.11.11.csr

跟随命令行输入主体的Distinguished Name,这里需要注意的是Common Name必须输入网站服务器的机器名或者IP,如果是本地开发则输入本地的IP/机器名/localhost均可。

有了CSR之后就可以用之前的CA来签发证书了,因为chrome浏览器检查证书与服务器地址是否匹配时,会从Subject Alternative Name中读取证书中描述的服务器地址,所以我们在签署证书的时候需要将Subject Alternative Name写入到证书中的Extensions。为了写入Extension我们需要用到配置文件。

在当前目录建立x509.config 文件,输入以下内容:

[ x509_ext ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = localhost
IP.1 = 127.0.0.1
IP.2 = 11.11.11.11

我们打算添加三个Subject Alternative Name 到证书中(localhost, 127.0.0.1, 11.11.11.11),这样做的目的是无论使用这三个中的任何一个访问网站,都可以通过证书的检测。

接下来我们用这个配置文件,将CSR签署为一个真正的证书。

$ openssl x509 -req -in 11.11.11.11.csr -CA ../ca.crt -CAkey ../ca.key -CAcreateserial -extfile x509.config -extensions x509_ext -out 11.11.11.11.crt -sha256

此处需要注意最后一个参数-sha256,因为chrome要求证书的散列算法必须是sha256,所以此处加入这个参数,否则在用chrome访问网站的时候会产生一个警告。

命令执行结束后,在当前目录会生成一个11.11.11.11.crt的证书文件,即用我们自己的CA为我们自己的网站签署的证书。

因为这个过程中使用到的CA是我们自己创建的,所以默认情况下系统是不会信任由这个CA签发的证书的,因此我们需要将CA的证书添加到系统的信任库中,对于不同的系统方法会有所不同。详细信息请参考这里。

测试

为了方便我们可以启动一个https server来进行测试。如果本地装有Node的环境,可以用npm安装http-server到全局。

$ npm install –g http-server

安装完成之后,可以通过命令启动http-server,并且启用SSL,同时指定private key和certificate。

$ http-server -S -C 11.11.11.11.crt -K 11.11.11.11-insecure.key

启动之后我们可以用curl来检测是否成功。

$ curl https://127.0.0.1:8080

如果没有任何错误,并且能够看到返回的response,则证明用自己的CA签署的证书已经被系统信任。