解密TLS协议全记录之Openssl的使用与Nginx Server的配置

引言

  • Openssl是TLS协议进行报文加密,安全通讯而用到的开源代码包,代码主要由C语言编写,我个人也只看了其中一部分代码,当作工具使用,没有深入分析。
    其维基百科的链接:https://zh.wikipedia.org/zh-cn/OpenSSL,
    openssl的官网wiki链接:https://wiki.openssl.org/index.php/Enc#Cipher_alogorithms
  • Nginx是一款面向性能设计的HTTP服务器,相较于Apache、lighttpd具有占有内存少,稳定性高等优势。与旧版本(≤ 2.2)的Apache不同,Nginx不采用每客户机一线程的设计模型,而是充分使用异步逻辑从而削减了上下文调度开销,所以并发服务能力更强,其中也有利用到openssl的代码进行安全通讯。

安装与使用

需要注意的是,接下来的编译,安装都将支持TLS1.3协议,由于TLS1.3目前还未完全成熟,因此需要我们进行额外的编译操作来使系统支持。

1. openssl

1.1 openssl 编译与安装

1.1.1 编译安装openssl 1.1.1版本, 来支持TLS1.3协议。

  • openssl的源码链接:https://www.openssl.org/source/, 选择 Openssl 1.1.1版本,目前这个版本支持最新的TLS1.3协议草案。
    编译安装指令:
#查看openssl的版本信息
pi@raspberrypi:~ $ openssl version
OpenSSL 1.0.1t  3 May 2016
# 卸载系统自带的openssl,避免链接冲突问题。
sudo apt-get remove openssl
wget  https://www.openssl.org/source/openssl-1.1.1g.tar.gz
tar -xf openssl-1.1.1g.tar.gz
cd openssl-1.1.1g
./Configure # 在需要确定编译器环境的时候,请执行./config, 参阅Q&A
make
make install

查看openssl的最新版本信息, 查看openssl支持的加密套件,并确定是否支持 TLS1.3协议

pi@raspberrypi:/usr/local $ openssl version
OpenSSL 1.1.1g  21 Apr 2020
pi@raspberrypi:/usr/local $ openssl ciphers -V
          0x13,0x02 - TLS_AES_256_GCM_SHA384  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(256) Mac=AEAD
          0x13,0x03 - TLS_CHACHA20_POLY1305_SHA256 TLSv1.3 Kx=any      Au=any  Enc=CHACHA20/POLY1305(256) Mac=AEAD
          0x13,0x01 - TLS_AES_128_GCM_SHA256  TLSv1.3 Kx=any      Au=any  Enc=AESGCM(128) Mac=AEAD
          0xC0,0x2C - ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(256) Mac=AEAD
          0xC0,0x30 - ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  

对以上的输出做一下说明:

Kx 表示密钥交换的加密算法(需要结合wireshark以及TLS协议进行流程分析,在TLS1.2中,主要是指RSA和DH密钥交换算法, 根据rfc5246中的描述,Kx等于any时,表示匿名密钥交换算法, 一般通过Diffie-Hellman来实现密钥交换。
Au表示身份认证算法,Enc表示通信数据的加密算法, Mac表示消息验证算法(计算消息摘要的算法)。

通过openssl 测试网站是否支持 TLS1.3 协议

openssl s_client -connect www.nike.com:443 -tls1_3

1.1.2 总结Q&A

Q: 使用./configure 配置编译环境所使用的Makefile的时候,出现以下警告,此时会提醒 需要选择和系统适配的编译器.

Notice: Configure脚本是用来生成Makefile的脚本,用于确定系统,编译器, 编译安装目录等等编译环境,便于适应多环境和平台, 类似CMake,其目的就是 用来构建工程项目框架,通过./Configure --help可以查看相关帮助信息。
报错的Log如下:

pi@raspberrypi:~/pkg_compile/openssl-1.1.1g $ ./Configure
Usage: Configure [no- …] [enable- …] [-Dxxx] [-lxxx] [-Lxxx] [-fxxx] [-Kxxx] [no-hw-xxx|no-hw] [[no-]threads] [[no[no-]zlib|zlib-dynamic] [no-asm] [no-egd] [sctp] [386] [–prefix=DIR] [–openssldir=OPENSSLDIR] [–with-xxx[=vvv]] [–config=FILE] os/lags]
pick os/compiler from:
BS2000-OSD BSD-generic32 BSD-generic64
NOTE: If in doubt, on Unix-ish systems use ‘./config’,

A: 解决办法,可以通过
./Configure CC=编译器绝对路径来选择编译器,
也可以直接执行 ./config 脚本, 该脚本会帮忙确定当前系统的编译环境,然后执行./Configure脚本。

pi@raspberrypi:~/pkg_compile/openssl-1.1.1g $ ./config
Operating system: armv7l-whatever-linux2
Configuring OpenSSL version 1.1.1g (0x1010107fL) for linux-armv4
Using os-specific seed configuration
Creating configdata.pm
Creating Makefile

Q:执行openssl的时候,出现openssl: error while loading shared libraries: libssl.so.1.1: cannot open shared object file: No such file or directory

A: 在标准的lib库路径找不到库,需要执行

ln -s /usr/local/lib/libssl.so.1.1 /usr/lib/libssl.so.1.1
ln -s /usr/local/lib/libcrypto.so.1.1 /usr/lib/libcrypto.so.1.1

或者配置 LD_LIBRARY_PATH 的环境变量,来添加动态库的搜索路径。

2. nginx服务器的搭建

2.1 nginx编译与安装

通过官网的Change-log文件:https://nginx.org/en/CHANGES-1.16, 可以知道最新版本的nginx 默认支持TLS1.3协议。

Changes with nginx 1.15.6 06 Nov 2018
*) Security: when using HTTP/2 a client might cause excessive memory
consumption (CVE-2018-16843) and CPU usage (CVE-2018-16844).
*) Security: processing of a specially crafted mp4 file with the
ngx_http_mp4_module might result in worker process memory disclosure
(CVE-2018-16845).
*) Feature: the “proxy_socket_keepalive”, “fastcgi_socket_keepalive”,
“grpc_socket_keepalive”, “memcached_socket_keepalive”,
“scgi_socket_keepalive”, and “uwsgi_socket_keepalive” directives.
*) Bugfix: if nginx was built with OpenSSL 1.1.0 and used with OpenSSL
1.1.1, the TLS 1.3 protocol was always enabled.

编译相关的内容也可以查阅官网的Documention, 编译与安装的命令:

cd pkg-compile
wget https://netix.dl.sourceforge.net/project/pcre/pcre/8.40/pcre-8.40.tar.gz #需要支持perl工具
wget http://www.zlib.net/zlib-1.2.11.tar.gz  #需要支持 zlib库
wget https://nginx.org/download/nginx-1.16.1.tar.gz
tar -xf nginx-1.16.1.tar.gz
tar -xf pcre-8.40.tar.gz
tar -xf zlib-1.2.11.tar.gz
./configure --sbin-path=/usr/local/nginx/nginx
    		--conf-path=/usr/local/nginx/nginx.conf
   			--pid-path=/usr/local/nginx/nginx.pid
    		--with-http_ssl_module
    		--with-pcre=../pcre-8.40
    		--with-zlib=../zlib-1.2.11
make
make install

2.2 nginx 配置与测试

2.2.1 支持http服务器

vim /usr/local/nginx/nginx.conf
killall nginx
/usr/local/nginx/nginx -c nginx.conf

修改server 块中 server_name 为 eth0接口的ip地址。

server {
listen 80;
server_name 192.168.1.196;

}

也可以修改nginx文件下的html的indexl.html页面代码


<html>
<head>
<title>Welcome to Starry!title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
style>
head>
<body>
<h1>Welcome to Starry!h1>
<p> Fighting For Starry!p>
<p> Go! Go! Go!p>

<p><em>Thank you for your love.em>p>
body>
html>

效果如图:
解密TLS协议全记录之Openssl的使用与Nginx Server的配置_第1张图片

2.2.2 通过openssl制作签名证书

  • 在搭建https服务器之前,我们必须要为服务器创建证书,该证书需要通过TLS协议下发到客户端以便进行加密通信,因为创建的是个人的测试服务器,所以,我们通过openssl 制作的证书,就自己签名就可以了,正常来说,切记不要往浏览器上添加不清楚的签名机构的证书。
  • 在TLS协议中,当加密通信时,需要公钥信息以及需要确保通信双方的合法性,证书中便会包含这些内容, 一般来说,客户端需要校验服务端的证书内容 (正常来说, 正常的网页访问,客户端会校验服务器的证书,而网银的话,服务器还会校验客户端的证书, 来确保客户端是合法的。), 正常来说, 浏览器会包含一些默认的合法证书和默认的证书签发机构的信息,用于校验绝大多数网站的合法性。
第一步, 创建客户端证书请求或者自签名证书。

Openssl官网手册: https://www.openssl.org/docs/man1.1.1/

openssl genrsa -out cert.key #1024
#如果生成ras密钥到ca.key, 并使用des3算法加密(之后每次使用该密钥,都要输入密码)
#openssl genrsa -des3 -out cert.key 1024 
#这条命令会需要如下,需要额外输入密码,对私钥的进行加密。
#pi@raspberrypi:/usr/local/nginx/tls_file $ sudo openssl genrsa -des3 -out ca.key
#Generating RSA private key, 2048 bit long modulus (2 primes)
#..+++++
#......................+++++
#e is 65537 (0x010001)
#Enter pass phrase for ca.key:
#Verifying - Enter pass phrase for ca.key: 
# openssl rsa -in cert.key -pubout -out pubkey.pem #通过rsa参数,可以生成公钥信息,rsa参数专门用于实现rsa算法相关操作。
######################################################################
# openssl中 rsautl 参数用于实现基于rsa的加密解密的相关扩展使用工具,
# 通过rsa工具,使用公钥加密文件
# openssl rsautl -encrypt -in input.file -inkey pubkey.pem -pubin -out output.file 
# 通过rsa工具,使用私钥来解密文件
# openssl rsautl -decrypt -in input.file -inkey key.pem -out output.file
######################################################################
# 用私钥来生成基于509证书标准的证书请求(req参数), 这个证书请求将包含了公钥信息,但是要注意这里只是证书请求,不是证书。
# 因为接下来还需签名机构加密头部, 类似于盖章,认证合法性, 所以称之为 证书请求。
# -new  表示生成证书请求,  
# -x509 表示 生成证书请求的同时,生成自签名证书
# Notice: 在生成证书请求的时候,将会包含公钥等信息, 但是缺乏签名。
openssl req -new -days 365 -key cert.key -out cert.csr 

以上命令会输出以下信息, 由于后续的CA在验证时会被使用到,这边揪出来特别说明。
为何会知道这几个选项会被使用, 查看 cat /usr/local/ssl/openssl.cnf 的时候,就会发现

# For the CA policy
[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

openssl在进行证书验证的时候, 需要签发机构的证书中 countryName,stateOrProvinceName,organizationName要和证书请求中的值匹配, commonName 作为服务器域名也一定要提供, 并且不能和CA证书中的值一样,否则,在CA校验的时候,因为common name一样,会被认为是自签名证书。

Ignoring -days; not generating a certificate
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]:FJ 省份名称
Locality Name (eg, city) []:QZ 所属市区
Organization Name (eg, company) [Internet Widgits Pty Ltd]:walleva 组织名称
Organizational Unit Name (eg, section) []:96
Common Name (e.g. server FQDN or YOUR name) []: Starry 全限定域名:同时带有主机名和域名的名称,为持有者取名。
Email Address []:[email protected]
Please enter the following ‘extra’ attributes
to be sent with your certificate request
A challenge password []:96 挑战密码,用于安全需要的时候,会被拿出来使用,这是一个可选项
string is too short, it needs to be at least 4 bytes long
A challenge password []:9696
An optional company name []:


# 输出证书请求的细节信息, 可以看到公钥等相关信息。
openssl req -in cert.crt -noout -text
# 可以直接就生成请求的同时, 就立马生成自签名证书,不经过机构进行认证签名, 有效期为 365天, 
# 该证书未经过签名机构的证书的签名, 无法导入CA机构的签名证书到浏览器中被得到校验,纯粹就TLS通信测试时使用,不可用于生产环境。
# 如下:
# openssl req -x509 -days 365 -key cert.key -out cert.crt

# 正规的流程: 
# 制作签名机构的私钥和签名证书(公钥)
# ---->签名(会对证书请求文件使用hash函数来提取摘要,然后对摘要使用私钥加密,得到签名,不影响证书内容,浏览器在验证的使用,则把签名机构的公钥证书拿出来解密,`私钥负责签名,公钥负责验证,`注意 RSA 加密方案和 RSA 签名方案是不同的)
# ------->制作私钥以及请求签名的证书 --->得到用于通信的证书。

# 基于509证书参数, 输出证书请求的详细细节, 可以看到RSA 公钥信息。
# openssl x509 -in cert.crt -noout -text 
# Subject Public Key Info:
#           Public Key Algorithm: rsaEncryption
#               RSA Public-Key: (1024 bit)
#                Modulus:
#                    00:b8:c9:22:14:28:5c:57:88:82:e9:0c:e6:39:b3:
#                    a7:37:71:62:a4:10:89:20:18:97:5a:ec:95:49:e8:
#                    64:a2:ea:bb:33:49:43:83:2d:81:2c:6f:43:6a:a8:
#                    40:00:11:8c:03:9e:2b:6f:d9:94:f9:77:49:b0:97:
#                    08:6a:9d:0b:cb:8f:e1:a8:3f:81:53:98:16:88:aa:
#                    a8:a1:c7:80:b6:38:b5:7b:20:02:ea:d7:6a:30:14:
#                    5b:1d:c9:b1:63:63:07:36:fc:55:51:e8:6b:10:44:
#                    8e:f7:a8:06:83:a6:61:a1:1a:d8:24:8a:d2:dd:d6:
#                    b0:f9:72:e4:9a:4f:13:7e:5d
#                Exponent: 65537 (0x10001)
##########################################################################
第二步,创建用于签名的证书给证书请求的文件做签名

给证书请求的文件进行签名有两种,经过验证都可以使用。
第一种:创建私有CA证书进行签名的方式

#  创建私有CA证书,使用该 CA证书 给证书请求文件进行签名
#  openssl genrsa -des3 -out ca.key 4096  # 创建私有CA的私钥,并选择用des3 算法加密私钥。
openssl genrsa -out ca.key 4096  # 创建私有CA的私钥
# 生成私有CA的自签名证书,和前面的生成自签名证书,其实是一样的道理, 只是这个我们要把它当作 最顶层的签名证书。
openssl req -x509 -days 365 -key ca.key -out ca.crt 
# 使用CA的私有证书和私钥进行证书请求文件的签名, 就可以得到最终的签名证书。
# 个人认为这是比较简化,随性的一种实现方式。
# set_serial 01 为证书设置序列号。
openssl x509 -req -days 365  -CA ca.crt -CAkey ca.key -set_serial 01 -in cert.csr  -out cert_1.crt 
# 对生成的证书进行校验,看是否能正常通过校验。
openssl verify -CAfile ca.crt cert_1.crt 

第二种:该方式是比较正规的方式, 将会建立一个完整的CA机构目录,进行统一管理。

openssl genrsa -out ca.key # 创建私有CA的私钥,前面已创建CA私钥的话,可以省略。
openssl req -new -x509 -days 365 -key ca.key -out ca.crt #生成私有CA的自签名证书,前面已创建CA证书的话,可以省略。

# 利用CA签名证书
# openssl 将会指定默认的openssl.cnf 文件,来获取一些默认的参数配置。
openssl ca -keyfile ca.key -cert ca.crt -in cert.csr -out cert_2.crt -config /usr/local/openssl/openssl.cnf
# 以上命令会报错如下: 
# Using configuration from /usr/local/ssl/openssl.cnf
# 1995927552:error:02001002:system library:fopen:No such file
# ordirectory:crypto/bio/bss_file.c:69:fopen('./demoCA/index.txt','r')
# 1995927552:error:2006D080:BIO routines:BIO_new_file:no such file:crypto/bio/bss_file.c:76:
mkdir -p demoCA/newcerts
touch ./demoCA/index.txt
touch ./demoCA/serial
echo 01 > demoCA/serial  # 跟前面的方法一样,要设置证书的序列号。
# 对生成的证书进行校验,看是否能正常通过校验。
openssl verify -CAfile ca.crt cert_2.crt  cert_1.crt
# 成功的话会出现以下log报告:
# cert_2.crt: OK
# cert_1.crt: OK

**进行校验的时候, 可能会碰到如下问题: **

Q: unable to load certificate?The countryName field is different
A: 因为个人证书请求和CA的签名证书的 countryName, localityName 等不一致, openssl.cnf有要求这几个值要一样,才能正确签名和校验
Q:error 18 at 0 depth lookup: self signed certificate
A: 因为个人证书请求和CA证书 中的 commonName 的值一样, 在证书校验的时候,就会当作是自签名证书,无法校验,回顾一下自签名证书的命令,确实就是这样,生成证书请求的同时,便会同时生成证书文件, 在这个时候,证书中的持有者和颁发者是一样的。

2.2.3 利用openssl加密文件

Openssl除了进行加密证书的制作,也可以加密,解密私密的文件。
notice: Openssl 设计得很巧妙, 比如想实现 rsa, aes-256-cbc算法相关操作,先设置算法名称,参数前没有‘-’符号,然后再配置该算法名称的具体小参数。 当先选择算法名称之后, 通过 openssl aes-256-cbc -help, 可以查看到其他的配套的功能参数。 同样的, 当先选择其他参数,比如rsautl, 通过openssl rsautl -help,可以查看到该大参数下对应的小参数名称。

1. 对称加密,比如aes, dh加密算法, 一般理解为加密和解密都使用相同的密钥,
DH加密算法也是对称加密算法, 客户端和服务器可以通过算法得到一样的密钥, 一般用来作为TLS通信中的密钥交换算法。

# -e 表示加密输入文件到输出文件上
openssl aes-256-cbc -e -in des.txt -out enc.txt
# -d 表示解密输入文件到输出文件上
openssl aes-256-cbc -d -in enc.txt -out des.txt

2. 非对称加密,比如rsa算法, 一般理解为公钥加密,私钥解密.

# 生成rsa密钥
sudo openssl genrsa -out rsa_pri.key
sudo openssl rsa -help
# 通过私钥得到公钥
sudo openssl rsa -in rsa_pri.key -pubout -out rsa_pub.key
# 利用pubin参数,输入公钥来加密明文
sudo openssl rsautl -encrypt -in plaintext -out ciphertext -inkey rsa_pub.key -pubin
# 使用私钥来解密密文。
sudo openssl rsautl -decrypt -in ciphertext -out plaintext -inkey rsa_pri.key 
history

2.2.4 配置nginx的https服务器

    #HTTPS server

    server {
        listen       443 ssl;
        server_name  localhost;

        ssl_certificate      /usr/local/nginx/tls_file/cert_sign.crt;
        ssl_certificate_key  /usr/local/nginx/tls_file/cert.key;

        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

        location / {
            root   html;
            index  index.html index.htm;
        }
    }
################################################

2.2.5 也可以配置https服务器来支持某一个具体的加密套件(在学习密钥交换的时候,很有用)

 server {
        listen       443 ssl;
        server_name  localhost;

        ssl_certificate      /usr/local/nginx/tls_file/cert_2.crt;
        ssl_certificate_key  /usr/local/nginx/tls_file/cert.key;

        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        #ssl_ciphers AES256-GCM-SHA384;
        ssl_ciphers ECDHE-RSA-CHACHA20-POLY1305;
        #ssl_ciphers HIGH:!aNULL:!MD5;
        ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers  on;

        location / {
            root   html;
            index  index.html index.htm;
        }
    }

配置完之后, 重启nginx,注意一旦有报什么错误警告,可以通过nginx记录的log 进行分析,
此时通过浏览器访问的时候,会出现以下警告。
解密TLS协议全记录之Openssl的使用与Nginx Server的配置_第2张图片
这是因为浏览器中保存的CA证书机构无法校验我们下发的证书是否有效, 因为用来签名的CA机构也是我们自己创建的, 正常的生产环境中, 证书请求的文件要让正规机构去签名,才能正确工作, 此处测试,先忽略该错误。
为了消除上述的错误的话, 可以手动在浏览器中导入私有的签名机构的证书,方便浏览器进行 校验。
notice: 常规的生产环境不可随意导入他人的CA证书。
将刚刚创建的CA机构的证书导出到本地。
解密TLS协议全记录之Openssl的使用与Nginx Server的配置_第3张图片
打开浏览器的证书管理,将其添加到受信任的证书机构中。
解密TLS协议全记录之Openssl的使用与Nginx Server的配置_第4张图片
这样就可以进行证书的检验,但是,有一点疑问是我的google 浏览器会一直报以下错误:
一直说我的CA机构无效, 关于这一点,就没去追了, 有了解的伙伴可以在文章下方留言。
解密TLS协议全记录之Openssl的使用与Nginx Server的配置_第5张图片

易混淆点:

同样的X.509证书,可能有不同的编码格式,目前有以下两种编码格式.
PEM - Privacy Enhanced Mail,打开看文本格式,以"-----BEGIN…"开头, "-----END…"结尾,内容是BASE64编码.
查看PEM格式证书的信息:openssl x509 -in certificate.pem -text -noout
Apache和NIX服务器偏向于使用这种编码格式.
DER - Distinguished Encoding Rules,打开看是二进制格式,不可读.
查看DER格式证书的信息:openssl x509 -in certificate.der -inform der -text -noout
Java和Windows服务器偏向于使用这种编码格式.
PEM转为DER: openssl x509 -in cert.crt -outform der -out cert.der
DER转为PEM : openssl x509 -in cert.crt -inform der -outform pem -out cert.pem
以下名称,我一般用来作为文件的后缀,以便更好地区分是什么内容的文件。
CRT - CRT应该是certificate的三个字母,其实还是证书的意思,常见于
NIX系统,有可能是PEM编码,也有可能是DER编码,大多数应该是PEM编码,相信你已经知道怎么辨别.
CER - 还是certificate,还是证书,常见于Windows系统,同样的,可能是PEM编码,也可能是DER编码,大多数应该是DER编码.
KEY - 通常用来存放一个公钥或者私钥,并非X.509证书,编码同样的,可能是PEM,也可能是DER.
查看KEY的办法:openssl rsa -in mykey.key -text -noout
如果是DER格式的话,同理应该这样了:openssl rsa -in mykey.key -text -noout -inform der
CSR- Certificate Signing Request, 即证书签名请求,这个并不是证书,而是向权威证书颁发机构获得签名证书的申请,其核心内容是一个公钥(当然还附带了一些别的信息),在生成这个申请的时候,同时也会生成一个私钥,私钥要自己保管好.做过iOS APP的朋友都应该知道是怎么向苹果申请开发者证书的吧.

你可能感兴趣的:(Web,Develop)