证书与安全传输及SSL

559 阅读9分钟

名称解释

ITU International Telecommunication Union 国际电信联盟 ITU是以国家参与为主体

IETF Internet Engineering Task Force 互联网工程任务组 民间学术组织 主要任务是负责互联网相关技术规范的研发和制定 RFC文档都是他们的产物

X.509 标准 ITU-T标准化部门基于他们之前的ASN.1定义的一套证书标准。事实上X.509认证指的是IETF RFC5280里定义的X.509 v3 zh.wikipedia.org/wiki/X.509

PKCS Public-Key Cryptography Standards 公钥加密标准皆由RSA信息安全公司所制定 参考 zh.wikipedia.org/wiki/公钥密码学标…

PKI 公钥基础设施

CRL 证书吊销列表

通信安全的一个主要标准是SSL,其核心在于非对称加密算法RSA和CA

rsa加密算法的应用如SSH证书认证
使用ssh证书登录的一个步骤就是创建公私钥对,如下所示
ssh-keygen -t rsa -b 2048 -N '' -f ~/.ssh/id_rsa
生成一个名为id_rsa的私钥和一个名为id_rsa.pub的公钥

ssl的应用包括openvpn、https、微服务之间的服务访问认证

无CA的非对称加密安全通信

其代表应用是加密邮件
前置条件:
A和B都拥有一个公钥私钥对,A,B都公开自己的公钥 B给A发信:
B将邮件内容使用A的公钥加密,然后用B自己的私钥签名后(防抵赖)发送给A
A使用自己的私钥解密邮件内容
A回信步骤:
A将邮件内容使用B的公钥加密,然后用A自己的私钥签名后(防抵赖)发送给B
B使用自己的私钥解密邮件内容

非对称加密的安全隐患
在一切的最开始,A和B要通过网络交换publickey。如果C在中间拦截了呢?假设有这种情况,C拦截了A和B的publickey,又分别用自己的publickey发给A和B。A和B并不知道,他们还以为这个publickey来自对方。当A给B发消息时,A先用自己的privatekey加密数据的hash值,之后用C传来的假的publickey加密数据,再发出去。C拦截到之后,先用C自己的privatekey解密数据,C就获取了A的原始信息!之后,C可以篡改数据内容,再用自己的privatekey加密数据的hash值,用之前拦截的B的publickey加密数据,再发给B。B收到以后,先用自己的privatekey解密数据,再用C传来的假publickey解密hash值,发现匹配。这样,B收到了一条来自C的假的信息,但是B还以为信息来自于A。中间人攻击仍然可能存在!

所以这个时候在无CA的情况下要避免中间人攻击的一个解决方法就是将publickey也通过一种极其安全的方式交给对方(例如U盘拷贝)。但在这种极其安全的方式在网络通信中本身就是一种近乎奢侈的条件。

有CA非对称加密通信

OpenVPN

  1. 创建ca: ./easyrsa build-ca nopass
    这个过程生成了ca.crt和ca.key 也就是一个公钥私钥对,其中.crt是公钥,.key是私钥

digital ocean官方解释:
ca.crt: 是CA的公共证书文件,在OpenVPN的通信中,服务器和客户端用来互相通知它们是同一信任网络的一部分,而不是进行中间人攻击的那个中间人。因此,您的服务器和所有客户端都需要该ca.crt文件的副本。
ca.key: 是CA机器用来为服务器和客户端签名密钥和证书的私钥。如果攻击者获得对您的CA以及ca.key文件的访问权限,则他们将能够签署证书请求并获得对您的VPN的访问权限,从而损害了VPN的安全性。这就是为什么ca.key文件应仅位于CA计算机上的原因

  1. 签发server端证书
    2.1 首先生成证书签名请求: ./easyrsa gen-req server nopass
    这个过程生成了服务端私钥server.key和服务端证书签名请求文件server.req 服务端证书签名请求文件server.req,使用ca的私钥ca.key进行签名
    2.2 签名(签发证书)的步骤:./easyrsa sign-req server server(此处略去导入签名请求的步骤)
    这个过程生成了服务端证书server.crt,这个crt证书文件本质是已经经过ca认证(即使用ca私钥加密)过的server公钥,标准说法叫做数字证书。

  2. 签发client端证书
    过程与server端完全一致,此处不再赘述

server和client通信时的安全步骤:

  1. 建立连接后,client和server交换双方的数字证书,即client向server传输client.crt,server向client传输server.crt;
  2. server端收到client.crt后,使用ca.crt解密后得到client端的公钥。server端使用client的公钥加密信息并传输给client;
  3. client端使用client.key进行解密获取原始信息。 建立连接后,server向client端发消息时:
    与client向server端发送消息雷同,不再赘述。

实际使用 非对称加密算法比对称加密算法要复杂的多,处理起来也要慢得多。如果所有的网络数据都用非对称加密算法来加密,那效率会很低。所以在实际中,非对称加密只会用来传递一条信息,那就是用于对称加密的密钥。当用于对称加密的密钥确定了,A和B还是通过对称加密算法进行网络通信。这样,既保证了网络通信的安全性,又不影响效率,A和B也不用见面商量密钥了。

也就是说。非对称加密算法加密的消息仅是server端和client端的对称加密的算法和密钥,server端和client端交换了对称加密的算法和密钥后就开始对称加密通信了。因此对称加密通信为server和client通信时的安全步骤的第4步。

密钥交换期间所使用的Diffie-Hellman密钥dh.pem和传输层安全TLS密钥ta.key的使用暂不在本篇文章讨论范围

以openvpn为例,我们可以看到ca在公钥的传输中扮演了极其重要的角色。

HTTPS

那么在https中是如何保证ca的真实性呢 具体实施就涉及到RootCA和信任链的知识,具体内容见参考 8 其中RootCA就内置于浏览器或操作系统中,在windows中可以通过certmgr.msc看到内置的RootCA

https通信安全过程:

  1. 浏览器向web服务器请求证书
  2. web服务器下发经过ca签名(ca的私钥加密)过的web服务器证书
  3. 浏览器内置的ca公钥解密web服务器证书得到web服务器公钥
  4. 浏览器利用web服务器公钥加密对称加密算法和密钥发送给web服务器
  5. web服务器接收到浏览器发送的信息后,用自己的私钥解密,双方开始安全通信

12306早期就比较简单粗暴直接要求用户导入其根证书,现在使用的是Digicert Global RootCA签发的证书
在内部企业应用时,无法使用内部主机名向外部ca申请证书。通常是使用自签名证书或自建ca发放证书,然后导入自签名证书或ca证书

一些实践

公共证书

使用Let's encrypt获取免费SSL证书

假如有一个正常运行的www.domain.com的http站点,其绑定了域名www.domain.com和domain.com,站点文件的根目录位于/home/www/htdocs

  1. 获取脚本 curl https://get.acme.sh -o acme.sh
  2. 赋予可执行权限 chmod +x acme.sh
  3. 执行安装程序 ./acme.sh
  4. 退出终端后重新进入以使用新的环境变量
  5. 获取证书acme.sh --issue -d mydomain.com -d www.mydomain.com --webroot /home/www/htdocs/
  6. 安装证书到webserver
    下文中--reloadcmd 如果不清楚可以先不加,待弄明白后手动编辑~/.acme.sh/domain.com/domain.com.conf这个配置文件也是后期任务计划更新所需的配置,这个reload命令一定要在配置文件中存在且正确无误,否则即使更新了证书,因为没有正确重启webserver,会导致证书无法及时正确加载

apache的例子

acme.sh --installcert -d domain.com \
--cert-file      /path/to/certfile/in/apache/cert.pem  \
--key-file       /path/to/keyfile/in/apache/key.pem  \
--fullchain-file /path/to/fullchain/certfile/apache/fullchain.pem \
--reloadcmd     "service apache2 force-reload"

nginx的例子 注意 路径写站点加载时候证书要存放的路径, 不要写成申请下来的证书存放路径,出错的话原始key会被覆盖成空白文件,可以从backup文件夹中找回来

acme.sh --installcert -d domain.com \
--key-file       /path/to/keyfile/in/nginx/key.pem  \
--fullchain-file /path/to/fullchain/nginx/cert.pem \
--reloadcmd     "service nginx force-reload"
  1. 检查webserver的站点配置文件中是否正确的配置了https ,包括端口,证书私钥,证书,证书信任链文件
  2. 查看acme的证书更新任务计划 crontab -l
  3. acme的强制更新 acme.sh --cron --force

非对称加密和内部证书

公钥私钥对

生成公私钥对
首先使用openssl命令生成私钥
openssl genrsa -out rsa_private_key.pem 1024
再根据私钥生成公钥
openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

自签名证书生成过程

  1. 生成私钥
    openssl genrsa -out server.key 2048
  2. 生成 CSR (Certificate Signing Request)
    openssl req -subj "/C=CN/ST=HUBEI/L=WUHAN/O=WM/OU=Software/CN=test.com/emailAddress=test@test.com" -new -key server.key -out server.csr
  3. 生成自签名证书
    openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt

RootCA自建和证书发放过程

在RootCA中由于没有上级CA对其进行签名(对其公钥进行加密),只能自己生成公私钥对后,利用私钥对自己的证书签名请求(csr文件,主要内容是一个公钥)进行自签名。

  1. 部分操作系统需要先生成随机文件openssl rand -out ~/.rnd -hex 256
  2. ca服务器生成一个私钥
    openssl genrsa -out ca.key 2048
  3. ca生成自签名证书
    openssl req -subj "/C=CN/ST=HUBEI/L=WUHAN/O=WM/OU=Software/CN=ServerCA/emailAddress=test@test.com" -new -x509 -days 3650 -key ca.key -out ca.crt

然后开始正常签发证书(带SAN字段的证书,openssl.cnf见附录)

  1. 申请方自己创建私钥
    openssl genrsa -out server.key 2048
  2. 申请方创建证书签名请求文件
    openssl req -config openssl.cnf -new -key server.key -out server.csr
    查看请求文件详情openssl req -noout -text -in server.csr
  3. 在ca上执行签发命令
    openssl x509 -req -extfile openssl.cnf -extensions v3_req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -out server.crt -set_serial 01
    查看证书详情openssl x509 -noout -text -in server.crt

参考

  1. 阮一峰-RSA算法原理
  2. Mr Qiang-OpenSSL生成私钥和公钥
  3. 舌尖上的大胖-如何创建自签名的 SSL 证书
  4. zhang_dawei666-openssl 生成X509 V3的根证书及签名证书
  5. 杨俊明-openssl、x509、crt、cer、key、csr、ssl、tls 这些都是什么鬼?
  6. DigitalOcean-How To Set Up an OpenVPN Server on Ubuntu 18.04
  7. 肖宏辉-浅谈SSL/TLS工作原理
  8. 极客收藏夹-证书、CA、证书信任链
  9. Github-acmesh-official/acme.sh

附录

openssl.cnf内容

[req]
distinguished_name = req_distinguished_name
prompt = no
req_extensions = v3_req

[ v3_req ]

# Extensions to add to a certificate request

basicConstraints = CA:TRUE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[req_distinguished_name]
C = CN
ST = HUBEI
L = WUHAN
O = WM
OU = Software
CN = test.com

[alt_names]
DNS.1 = test.com
IP.1 = 192.168.123.72