https://背后的那些事儿

简介:这是一篇介绍跟SSL证书有关的事情,虽然是以https为标题,但是不会太细致说跟https请求过程中做了什么,而是跟证书有关的相关事情更加具有现实意义,而且也更有可操作性。

企业里的安全证书


证书类型

按照证书的验证方式一般分为

DV(Domain Validation)证书: 域名验证型证书,证书审核方式为通过验证域名所有权即可签发证书。此类型证书适合个人和小微企业申请,价格较低,申请快捷,但是证书中无法显示企业信息,安全性较差。如果是部署在web网站上,浏览器中显示锁型标志。

OV(Organization Validation)证书: 企业验证型证书,证书审核方式为通过验证域名所有权和申请企业的真实身份信息才能签发证书。目前OV类型证书是全球运用最广,兼容性最好的证书类型。此证书类型适合中型企业和互联网业务申请。如果是部署在web网站上,浏览器中显示锁型标志,并能通过点击查看到企业相关信息。支持ECC高安全强度加密算法,加密数据更加安全,加密性能更高。

EV(Extended Validation)证书: 增强验证型证书,证书审核级别为所有类型最严格验证方式,在OV类型的验证基础上额外验证其他企业的相关信息,比如银行开户许可证书。EV类型证书多使用于银行、金融、证券、支付等高安全标准行业。如果是部署在web网站上,其在地址栏可以显示独特的EV绿色标识地址栏,最大程度的标识出网站的可信级别。支持ECC高安全强度加密算法,加密数据更加安全,加密性能更高。

以上这段话是所搜引擎搜索到的,简单讲就是:小组织或者个人搞个小网站、小博客用DV证书最方便,普通公司或者企业用OV就足够了,对可信要求极高的用EV就很合适。但是,如何最方便的知道这个网站的证书是哪一种类型呢?

虽然上文说了通过浏览器绿色条和证书主体的名称来判断,但是最新的chrome,Firefox,edge浏览器等都放弃了绿色条,所以此类方法已经失效了,我测试发现有两种方法还是比较好用的。

方法一:

如果是chrome、edge包括Firefox浏览器,如果是验证合格的https页面,在浏览器左上角的小锁或者是两道杠,反正就是类似的地方。点开之后点击到证书相关的选项(比如下图,就是点击'连接是安全的'选项)

2

如果下面出现颁发对象:xxx,那么这个就是EV认证,是最高级别的认证

3

如果像下图这样都没有,那就需要点开继续往下看 4

如果点开显示颁发对象也就是证书主体的组织信息是有的那就是OV证书,如果未包含在证书中,那就是DV证书

5 6

方法二:

如果你觉得以上的办法不是很确定,就像回答语文题一样,答案貌似是但是又不是百分之百对的答案,我懂你,你一定想像做数学题那样,有一个确定的答案。那就还有一种简便方法,更加容易得到答案,那就是用Firefox浏览器

这个浏览器把证书的详细信息和含义解析的更加清楚: 还是在同样的方式点击左上角,再进入证书页面,会弹出一个对话框,点击查看证书

7

就会得到一个非常详细的页面,然后下拉到最底部,查看证书策略板块

这个是OV证书 8

这个是DV证书 9

这个是EV证书 10

签发流程

第一步:生成私钥 第二步:通过私钥生成CSR(请求证书文件) 第三步:签发证书 第四步:获取签发的证书(CA包,或者,根证书,中间证书,证书文件等)

名词解释

证书请求文件:第二步中的CSR,也称为请求证书文件,通俗理解就是:你去政务大厅办事,需要先填表写下你的信息,而这个表就是CSR,需要包含你的组织信息,地点,邮箱,以及你要签发的公钥

签名:签名是加密的过程,之所以签名是唯一的,是因为加密的秘钥是私钥,而这个私钥是证书授权机构(CA)的私钥,所以是安全的保密级别高的,也就是说只有他家能给你加密,所以也就确保了签发的唯一性。(暂且这么囫囵吞枣的解释)

CA包:一般签发完得到的证书不是一个文件,而是好几个文件,大概会包含CA机构的根证书,中间CA机构的证书,以及你的证书文件。为什么会有这么多呢,实际上,作为证书授权机构,需要有很高的要求和标准,而且还要有长期的运营商誉的保证,所以实际上根证书机构也就那么三四十家(具体可以参考,国际CA/浏览器产业联盟(cabforum.org),这个国际机构,因为主流浏览器预装的根证书基本上都是这里面的成员单位)。但是,申请签发的证书机构那么多,怎么可能只让这几个机构给你签发,那咋还能签发的过来,因此,会有很多中间CA机构,根证书CA给中间CA签发,,,然后中间CA给中间CA签发,,,然后中间CA机构再给你签发。可见,CA之间是有一个信任链的,如果从根CA一路验证能验证到你这里,就证明你的证书是可以信任的。(再具体的咱们后面再说)

使用场景

在实际应用中,有一个名词叫SSL卸载,说的就是把通过SSL/TLS加密过的数据解密还原到原来数据和信息的过程。而随着架构的不同,企业会把卸载的位置选在不同的地方。有可能是公网的入网口,也有可能是处理业务服务器的入口,还有可能是中转负载的位置。

实际场景不同,设备不同,位置也会有不同。比如,想要在入口处就卸载,那么入口处可能是waf(应用防火墙),那么证书就需要配置在waf设备上。如果是在流量分发或者是dmz核心区域,那几乎都是放在负载均衡(F5,A10,深信服负载,NGINX等等)处,而且会通过不同的域名对应的证书进行卸载和转发。当然,如果为了确保全链路的信息加密,还有可能是配置在自己应用服务器主机上,那就可能是在Apache,Tomcat,NGINX,IIS等软件web服务器中

除了面对公开的互联网服务外,还有企业之间,进行公对公的进行双向传输,那一般会需要双向的签名,认证,加密等。而这个过程就需要对互相之间证书的合理合法进行确认。其次,还有很多HSM加密模块,或者说机密机这类硬件设备,都需要生成和配置对应的证书文件。

OpenSSL


OpenSSL是一个开源的、功能强大的密码学工具包,具体来说就是它有很多能力,比如,加解密,SSL/TLS协议相关约定的功能实现,以及数字证书的生成,签名和验证等(总而言之很强大)

本地编译

之前看有的文章里写可以下载OpenSSL的msi格式(也就是windows安装文件格式)去安装,但是大部分的主流使用方法还是通过本地编译的方式编译出对应的OpenSSL,这样无论是静态库还是命令行工具都有。

准备工作:

  1. 首先、因为我本身电脑里就安装有Visual Studio 2022,所以就可以直接用它对应的命令行工具去编译

  2. 其次、需要配置perl,因为编译OpenSSL依赖于perl,所以需要下载安装perl,可以去草莓网站下载perl并安装,https://strawberryperl.com/

  3. 然后、下载源码,去官网https://www.openssl.org/,可以下载最新版本的源码。我之前下载的是3.0.0和现在的应该相差不多。

12

开始编译:

  1. 首选找到编译工具并进入到OpenSSL源码目录

13

  1. 然后用perl配置OpenSSL
1
2
3
4
5
6
7
8
# 若需要编译静态库需要增加no-shared参数,否则默认生成动态库

# perl Configure VC-WIN64A no-shared --prefix=E:\openssl-3.3.0\output\x64

perl Configure VC-WIN64A --prefix=E:\openssl-3.3.0\output\x64

--prefix 是编译后输出文件的地址
Configure 后接的参数是你的目标平台
  1. 开始编译

依次执行下列命令,需要一些等待时间

1
2
3
4
5
nmake 

nmake test

nmake install
  1. 完成后 完成后会在你的目标文件夹中生成一些文件,
  • bin文件夹里就是OpenSSL的可执行文件
  • html文件夹里就是OpenSSL的命令使用的文档
  • include文件夹就是在代码里使用OpenSSL的时候需要包含的头文件
  • lib文件夹就是在代码里使用OpenSSL的库文件

模拟签发过程

根证书机构

1
2
3
openssl genpkey -algorithm RSA -out root.key
# 或者
openssl genrsa -out root.key 2048l

去掉私钥的密码(方便后续)

1
openssl rsa -in root.key -out root.key

14

生成证书签名请求文件(CSR)

1
2
# 还可以添加很多扩展参数,但是为了方便流程的完整性,只把最精简理解原理的命令写了下来
openssl req -new -key root.key -out root.csr

此处需要注意,如果上述命令执行报错找不到openssl.cnf文件,你就需要配置一个环境变量 值为:OPENSSL_CONF指向你的openssl.cnf文件的地址,一般这个文件在你自己的openssl的安装包里有或者一般在C:\Program Files\Common Files\SSL\openssl.cnf

15

在生成CSR的时候会需要写注册信息
国家名称:输入您所在国家的双字母代码。 例如,cn
州或省名称:键入贵组织正式注册所在州的名称。 例如,jiangsu
地区名称:输入贵公司所在的地区或城市名称:例如:suzhou
组织名称:指定贵组织的正式名称:例如,root
组织单位名称:输入组织内负责管理 SSL 证书的单位名称。 例如,it
通用名称:输入您要保护的完全合格域名 (FQDN) 或您的姓名。 例如,root.com
电子邮件地址: test@root.com
挑战密码:password
可选的公司名称:可留空

16

生成自签名证书(模拟根证书)

1
openssl req -x509 -days 3650 -key root.key -in root.csr -out root.crt

以上生产的root.crt就是根证书,而root.key就是私钥

中间CA证书机构

跟上文一样依次生成私钥,证书签名请求文件CSR

1
2
3
4
5
6
# 生成私钥
openssl genrsa -out ca.key 2048
# 去掉密码
openssl rsa -in ca.key -out ca.key
# 生成CSR
openssl req -new -key ca.key -out ca.csr

用根证书和根私钥为ca签发证书

1
openssl x509 -req -in ca.csr -CA root.crt -CAkey root.key -CAcreateserial -out ca.crt -days 1850

最终生成的ca.crt就是中间CA机构的证书

17

自己的证书

依然执行跟上文一样的顺序,依次生成私钥,证书签名请求文件CSR

1
2
3
4
5
6
# 生成私钥
openssl genrsa -out my.key 2048
# 去掉密码
openssl rsa -in my.key -out my.key
# 生成CSR
openssl req -new -key my.key -out my.csr

用CA机构证书和私钥为自己的CSR进行签发新证书

1
openssl x509 -req -in my.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out my.crt -days 365

最终得到的my.crt就是我们自己的证书

最后还可以用openssl来验证下证书,可以先把ca.crt和root.crt用文本编辑器打开,然后粘贴到一个文件里。类似于下图这样

18

然后执行命令校验

1
2
openssl verify -CAfile chain.crt ca.crt
# chain.crt是拼合在一起的根证书到中间CA证书文件

如果出现下图ok字样,那就是验证没有问题了

19

其他命令

OpenSSL的强大不是我这几句文字就能简单说的清的,除了以上命令外,还有一些针对证书常用到的命令也在下面粘出来(主打一个简洁高效)

  • 一些常用命令
1
2
3
4
5
6
7
8
9
# 查看整个证书的结构
openssl x509 -in my.crt -text -noout

# 验证公钥和私钥的匹配情况(比如你刚刚申请签发的证书,你想验证下)
openssl pkey -pubout -in my.key | openssl sha256

openssl x509 -pubkey -in my.crt -noout | openssl sha256

openssl req -pubkey -in my.csr -noout | openssl sha256

验证结果:公钥的hash值一样,那么请求文件,证书,私钥对应的公钥hash值就都是一样的了,也就是互相匹配的

20

  • 从PKCS#7 转到pem格式
1
openssl x509 -outform pem -in yourfile.p7b -out yourfile.pem

结尾

这篇文章写到这里就差不多了,但是还是想补充几个有意思的点:

首先,上文描述的证书和SSL体系,都是基于RSA等国际算法的,而我们国家对于密码也有着很大的付出和努力,国密算法体系及相关证书架构也有很相似的建设体系,后续学习到一定阶段也会拿出来分享。

其次,如果是多子域名,但是证书都是统一管理的,比如都放在负载均衡上,那可以购买通配符证书,*.yuming.com之类的,就是贵,但是总比每个都买要划算。而且每年替换证书签发换新的时候,可以直接一键更换,负载均衡或者NGINX等都有类似的功能

再次,如果日常管理证书总是怕证书到期,可以自己写一个python脚本来验证是否到期,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import socket
import ssl
import datetime


def get_certificate_expiry_date(domain):
    try:
        # 解析域名获取IP地址
        ip_address = socket.gethostbyname(domain)

        # 建立SSL连接
        context = ssl.create_default_context()
        with socket.create_connection((ip_address, 443)) as sock:
            with context.wrap_socket(sock, server_hostname=domain) as ssock:
                # 获取服务器证书
                cert = ssock.getpeercert()
                # 解析证书有效期
                not_after = cert['notAfter']
                # 将有效期字符串转换为datetime对象
                expiry_date = datetime.datetime.strptime(not_after, '%b %d %H:%M:%S %Y %Z')
                return expiry_date
    except Exception as e:
        print("Error:", e)
        return None


# 输入域名
domains = ["www.domain1.com","www.domain2.com"]
for domain in domains:
    # 获取证书有效期
    expiry_date = get_certificate_expiry_date(domain)
    if expiry_date:
        print("Certificate expires on:", expiry_date)
    else:
        print("Failed to get certificate expiry date.")

注意: 代码中没有对请求超时等异常情况进行捕捉,可以自己优化,当然还可以用像长亭百川云这种网站,可以帮你检测网站情况,还可以到期提醒你(微信)

21

updatedupdated2024-10-072024-10-07