昔我往矣

OpenSSL的基础知识小课堂

2021年04月23日

在通信过程中,加密始终是一个绕不开的强需求,在互联网时代更是如此。怎么保证数据传输过程中的数据保密,这就引出了互联网的基础协议 ———— SSL 。本文记录了一些我个人在工作中常用到的一些相关工具和方法。

OpenSSLSecret

环境

CentOS 18.04
OpenSSL 1.1.1

对称加密和非对称加密

对称加密

了解非对称加密前,先简单说下什么是对称加密。对称加密的内涵就是使用同一个密钥进行加密和解密。比如简单的凯撒加密就是典型的对称加密。例如在通信前,加解密双方事先约定一个密钥为3,在加密的时候,加密者把每个字符右移3位生成密文,解密者把密文的每个字符左移3位还原出原始数据。

密钥:3
密文:Oryh
原文:Love

在现代计算机面前,这种加解密双方使用同一个密钥的对称加密方式是很容易被破解的。所以就需要非对称加密。

非对称加密

和对称加密不同,非对称加密的发送方和接收方分别使用不同的密钥。这就引入了两个新的概念,公钥和私钥。公钥是面向所有人公开的,私钥是拥有者秘密保管的,公钥和私钥成对生成。使用公钥加密的数据,只有私钥能解密; 使用私钥加密的文件,只有公钥能解密。在这两个新概念之下,又引入了2个新的操作:加密和签名。

  • 加密:消息发送者使用接收者的公钥对数据进行加密,这样的数据只有接收着的私钥能解密。
  • 签名:如果某数据能用某人A的公钥解密,那就说明这个数据是A的私钥加密,从而达到认证数据签名的作用。
  • 签名和加密同时使用:例如发送邮件时,发送者使用自己的私钥对邮件进行签名,同时使用接收者的公钥对邮件进行加密,可以达到非常高的安全级别。

非对称加密的用途已经非常广泛了,例如上面所说的邮件加密和签名可以使用GPG很轻易实现;另外我们ssh登录服务器时用到的ssh key和我们上网冲浪时看到的https://背后也都是非对称加密学在发挥作用。

证书创建

正常我们使用的都是权威机构颁发的证书,这些机构叫做CA(Certificate Authority,数字证书认证机构),颁发证书的过程专业一点说叫签发,因为操作系统和浏览器已经把这些机构的根证书内置了,所以CA颁发的证书能在所有计算机上畅通无阻。

使用openssl工具,我们自己也可以生成证书,这种自己签发的证书叫自签证书,是不受其它人信任。我们可以把自签证书配置到网站上,但是自签证书是不受其它浏览器信任的,浏览器此时会提示安全警告。接下来我们分别创建CA证书和服务器证书:

创建CA证书


$ (umask 066;openssl genrsa -out ca.key 2048)
$ openssl req -new -x509 -key ca.key -out ca.pem -days 3650
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Zhejiang
Locality Name (eg, city) []:Hangzhou
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
EmailAddress []:
$ ls
ca.key ca.pem

运行生成ca证书的命令后,出现了交互式的信息输入,需要填写一些CA证书的基本信息,这些都可以留空跳过。在目录下,我们有了密钥文件ca.key和证书文件ca.pem。我们可以对这两个文件做下简单的验证

$ openssl x509 -in ca.pem -noout -purpose
Certificate purposes:
SSL client : Yes
SSL client CA : Yes
SSL server : Yes
SSL server CA : Yes
Netscape SSL server : Yes
Netscape SSL server CA : Yes
S/MIME signing : Yes
S/MIME signing CA : Yes
S/MIME encryption : Yes
S/MIME encryption CA : Yes
CRL signing : Yes
CRL signing CA : Yes
Any Purpose : Yes
Any Purpose CA : Yes
OCSP helper : Yes
OCSP helper CA : Yes
Time Stamp signing : No
Time Stamp signing CA : Yes

可以看到新生成的证书基本可以用作各种用途,包括作为CA证书,CA证书是可以用来签发其它证书的。

创建服务证书

接下来,使用上面的CA证书创建服务器证书


$ openssl genrsa -out server.key 2048
$ openssl req -new -key server.key -out server.csr 
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Zhejiang
Locality Name (eg, city) []:Hangzhou
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:www.xnow.me
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
$ openssl x509 -req -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out server.pem -days 365
$ ls

ca.key ca.pem ca.srl server.csr server.key server.pem

在第二步操作中,和生成CA证书不同,我输入了Common Name字段,这个字段的缩写是CN。CN字段是指定这个证书可以用作什么域名的用途,譬如提供给域名是www.xnow.me的网站使用,如果证书和域名不匹配,也无法正常使用。

现在,我们多了三个文件,服务证书的密钥文件server.key,服务证书请求文件server.csr,服务证书文件server.pem

啰嗦下为什么有个csr文件,前面说过,密钥文件server.key只有证书拥有者才能有,其他人不能看,包括CA机构也不行。所以正常的流程是,证书申请者生成server.key,再使用server.key 生成server.csr,这个server.csr里就包含了申请的证书的详细信息,比如CN字段。由于这个server.csr文件是由server.key文件生成的,所以能保证根据csr文件生成的证书文件pem和密钥文件key匹配上。申请者只需要将csr文件提交给CA机构,就可以从CA机构获取的证书。避免了server.key泄漏出去。

现在已经拥有了一套ca证书和一套服务证书,接下来就可以使用这些证书配置服务器了,在此之前,先运行一些openssl工具对刚刚生成的证书做一些检查,在拿到机构签发的证书后,检查证书的基本信息是必要的步骤(好几次遇到签发的证书不符合需求)。

检查证书信息

  1. 检验 server.key 和 server.pem 是否匹配

$ openssl x509 -noout -modulus -in server.pem | openssl md5
(stdin)= 7f63c1f7bf7cc5b240c34612fac456ab
$ openssl rsa -noout -modulus -in server.key | openssl md5
(stdin)= 7f63c1f7bf7cc5b240c34612fac456ab

如果两个命令返回的结果一样,表明key文件和pem文件是一对。

  1. 检验server.pem是否是由ca.pem签发出来的
$ openssl verify -CAfile ca.pem server.pem 
server.pem: OK
  1. 查看证书的一些基本信息,颁发者、证书归属信息和生效到期时间
$ openssl x509 -in server.pem -noout -issuer -subject -dates
issuer=C = CN, ST = Zhejiang, L = Hangzhou, O = Internet Widgits Pty Ltd
subject=C = CN, ST = Zhejiang, L = Hangzhou, O = Internet Widgits Pty Ltd, CN = www.xnow.me
notBefore=Apr 16 06:23:21 2021 GMT
notAfter=Apr 16 06:23:21 2022 GMT

上面的信息展示了证书的颁发者(issuer),证书的主体(subject),证书的生效时间和过期时间(notBefore/notAfter)。

  1. 检查证书是否匹配某个域名

$ openssl x509 -in server.pem -noout -checkhost localhost
Hostname localhost does NOT match certificate
$ openssl x509 -in server.pem -noout -checkhost www.xnow.me
Hostname www.xnow.me does match certificate

上面的证书可以匹配 www.xnow.me,不匹配 localhost

  1. 检查证书的使用用途
$ openssl x509 -in server.pem -noout  -purpose
Certificate purposes:
SSL client : Yes
SSL client CA : No     
SSL server : Yes
SSL server CA : No
Netscape SSL server : Yes
Netscape SSL server CA : No
S/MIME signing : Yes
S/MIME signing CA : No
S/MIME encryption : Yes
S/MIME encryption CA : No
CRL signing : Yes
CRL signing CA : No
Any Purpose : Yes
Any Purpose CA : Yes
OCSP helper : Yes
OCSP helper CA : No
Time Stamp signing : No
Time Stamp signing CA : No

SSL server CASSL client CA的结果都是No,说明这不是一张CA证书。

  1. 前面都是我们自签的证书,下面拿一张letsencrypt签发的受信任证书看看

$ openssl x509 -in fullchain.pem -noout -issuer -subject -dates
issuer= /C=US/O=Let's Encrypt/CN=R3
subject= /CN=*.xnow.me
notBefore=Mar 16 15:00:42 2021 GMT
notAfter=Jun 14 15:00:42 2021 GMT
$ openssl x509 -in fullchain.pem -noout -checkhost xnow.me
Hostname xnow.me does match certificate
$ openssl x509 -in fullchain.pem -noout -checkhost test.xnow.me
Hostname test.xnow.me does match certificate

细心的话,从上面的结果可以发现,CN字段是支持通配符的,一张通配符证书可以提供给多个子域名使用。

使用证书

接下来,我们使用自己生成的证书配置一个站点

$ cat nginx.conf 
user  nginx;
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    keepalive_timeout  65;

    server {
        server_name www.xnow.me;
        listen 443 ssl;
        ssl_certificate server.pem;
        ssl_certificate_key server.key;
        location / {
           root   /usr/share/nginx/html;
        }
    }
}

这个nginx配置文件实在太简陋,仅供演示,不适合运行在生成环境中。在Nginx容器中运行使用这个配置文件和我们生成的server.pem和server.key 。

$ docker run -d --name=nginx --rm \ 
    -p 443:443 \
    -v $(pwd)/server.pem:/etc/nginx/server.pem \
    -v $(pwd)/server.key:/etc/nginx/server.key \
    -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf \
    nginx:1.19

正常运行起来之后,我们接着开始测试。

$ curl -i https://localhost  # 直接访问是失败的,因为这个是我们自签的证书
curl: (60) SSL certificate problem: unable to get local issuer certificate
……
$ curl -k https://localhost -k   # -k 可以帮我们忽略证书校验
HTTP/1.1 200 OK
Server: nginx/1.19.10
……
$ curl -i --cacert ca.pem --resolve "www.xnow.me:443:127.0.0.1" https://www.xnow.me 
HTTP/1.1 200 OK
Server: nginx/1.19.10
……

最后一条测试命令通过了,其中有2个关键点:

  1. --cacert指定了签发证书的ca证书。这个效果就如同把自己的ca证书导入到浏览器,让浏览器信任。
  2. --resolve将www.xnow.me域名解析到了127.0.0.1

 配置证书到系统

在上一步骤中,curl命令成功的对我们自签证书信任了,如果我们想操作系统全局信任CA怎么办?

$ sudo mkdir -p /usr/share/ca-certificates/self-signed
$ sudo cp ca.pem /usr/share/ca-certificates/self-signed/ca.crt  # 文件名需要crt结尾
$ sudo dpkg-reconfigure ca-certificates 
……
1 new root certificates were added to your trust store.
Import process completed.
Done
done.
$ sudo update-ca-certificates

根据输出信息,可以看到我们的ca证书已经导入到系统信任库了。此时再使用curl测试下,不用指定ca也是ok的。

$ curl -i --resolve "www.xnow.me:443:127.0.0.1" https://www.xnow.me 
HTTP/1.1 200 OK
Server: nginx/1.19.10
……

CA证书也可以导入到浏览器,让浏览器信任,这里就不做演示了。

当前暂无评论 »

添加新评论 »