数字证书
什么是是数字证书
数字身份
数字身份的本质是一对秘钥,分别为公钥和私钥。
把数字身份比喻成一个证件,那么数字证书就是“身份认证机构”盖在证件上的一个章(即权威机构的背书)。没有背书的数字身份是没实际意义的。
实际应用
生活中
一切进行数据通信的地方都有可能会用到数字证书,如
- 电子邮件
- 浏览网页
- 手机APP
- 科学上网
开发中
- 工具抓包:fiddler
为什么要有数字证书
演进过程
下面展示了网络安全通信是如何逐步衍生出数字证书的。
第一阶段,双方协商好对称密钥,之后的通信都使用对称密钥对明文进行加密解密操作。但是这个阶段的缺点是双方需要提前约定好密钥,该模式无法满足临时与陌生对象通信的需求。
第二阶段,双方使用非对称秘钥通信,其中bob有一对公私钥,通信初始他会把公钥提供给alice,alice使用该公钥来加密明文。由于密文只有bob的私钥能解密,所以在通信过程中其他人是无法解开密文的(未被篡改的情况下)。
该阶段的主要问题在于非对称加密明文的性能相较对称加密性能要差很多。
第三阶段,alice使用了临时生成的对称密钥来加密明文,然后再用bob的公钥加密对称密钥,然后把被加密的明文和被加密的密钥发送给bob,由于被加密的密钥只能用bob的私钥解密,所以过程是安全的(未被篡改的情况下),同时对称密钥加密明文保证了性能。但这个过程真的没缺陷了吗?我们看看以下场景:
该过程中,中间人可以拦截bob发给alice的公钥,同时发送自己的公钥给alice,伪装成bob。之后的通信过程中,alice使用伪装的公钥加密出的密文,自然能被中间人破解。更有甚者中间人可以伪造一份欺骗性的报文发给bob。该过程省略了alice用自己私钥加签的过程,本质上一样是不安全的,中间人既然能伪造bob给alice的公钥,当然也能伪造alice给bob的公钥,那么这个过程中的签名自然也是可以伪造的。
上一阶段的核心问题在于bob的公钥在传输过程中被篡改了,对于alice来说bob的公钥是不可信的。那么解决了公钥的信任问题,这个过程就安全了。
第四阶段:这一阶段bob把自己的公钥封装到数字证书里。在之后的通信初始阶段bob会先把数字证书发给alice,alice去CA机构验证数字证书的合法性,若验证通过,则代表bob的数字证书是可信的,那么证书里面的公钥自然也是可信的。这样就顺利的解决了公钥的信任问题。
为什么数字证书的认证过程是可信的
在服务域名上线之前,会先用自己的公钥生成一份证书签名请求文件(certificate sign request,csr),可以理解为填了份带有公钥的申请表单,发给CA机构,然后CA机构会把这份表单里面的关键信息(包括公钥、服务域名、所属机构等)提取成摘要,用自己的私钥进行加签。然后把签名和关键信息,输出到正式的数据结构中,形成了数字证书,再把这个数字证书发送给服务方,这个过程就叫做证书的签发。
服务器得到了具有公证力的证书,上线新的域名。随后客户端就可以访问该域名对服务器发起握手,握手阶段服务器会把证书发给客户端,客户端在证书里找到它的签发机构,遂去获取签发机构的证书。此时我们注意服务器证书的签名是用CA机构的私钥加签的,理所当然可以用CA机构的公钥进行验签。如果验签成功,则说明了服务器的证书是可信的。
可是此时出现了新的问题,我们通过这个流程可以验证服务器证书的有效性,前提是CA机构的证书也是合法的,但如何保证CA证书不会被篡改呢?
同理我们的客户端可以走相同的流程,去获取CA机构的上级机构的证书,来验证CA证书的有效性,这样就形成了一个递归验证链。这个递归链是有边界的,它的边界就是根证书。
由上图的信任链我们可以看出验证操作会一直递归到根证书。这里的关键就在于根证书是内置于我们操作系统本地的,那么在获取根证书的时候就不想要经过网络。若不经过网络,则就不存在被中间人攻击的风险。所以,到根证书为止,就可以证明整个信任链是可信的了。
小结
加密技术的存在是为了保障通信信息不被第三方窃取,保证信息的安全性,该技术甚至可以追溯到3900年前的古希腊。但是随着互联网尤其是无线网络的发展,出现了一种新的场景,就是通信双方节点,通常都是需要临时交换密钥来进行安全通信。伴随着这种场景,新的攻击手段也应运而生,那就是中间人攻击,简单来说就是中间人可以伪装成你的通信对象,交换非法秘钥,以达到监听窃取、篡改信息的目的。
而数字证书就是为了解决信任危机而的产生。
目前大部分数字证书都采用 x509第三版数据结构
数字证书是实现安全协议过程的基石。
Q&A
为什么要有中间证书?
1.根证书的私钥安全性隔离
A.降低替换私钥成本
B.降低暴露私钥风险
2.根证书的操作效率无法满足要求
浏览器如何获取中间证书?
一般来说,服务器会将中间证书一并发送过来。也就是说,当我们访问某个网站时,收到的不是一个证书,而是一系列证书。
当然,这不是强制要求,在服务器不发送的情况下,浏览器也会使用一些方法去定位中间证书,比如缓存之前下载过的证书
证书文件中的 Authority Information Access (AIA) Extension 里面含有上级证书的相关信息,浏览器可以使用这个信息去下载上级证书
PKI(Public Key Infrastructure)体系
当我们掌握了证书的理论性知识后。需要有一套完整的创建, 管理, 分发, 使用, 存储 和 吊销 证书的体系,这就是PKI体系。该体系里完整的囊括了所需的指导性工具,包括规则、策略、相关软硬件、和流程。该体系并不由某一家制定,而是互联网组织共同参与,其中做出过贡献的组织包括但不限于 ITU-T(X.500~X.599系列)、IETF、RSA实验室(RSA Security,PKCS系列)、ISO。综上所述,PKI可以认为是一系列的规范和标准。而为了实现安全基础服务目的的技术都可称为PKI。## 安全协议
TLS
TLS属于哪层协议?
TLS所处的协议层如下:
数据帧{ IP层{ TCP层{ TLS层{ 应用层{} } } } }
可见TLS介于传输层(TCP)和应用层之间。
如果把网络协议想象成一个纸团,协议之间的嵌套就像一张纸包住另一个纸团一样。最里面的一层就是应用层协议,如HTTP。而网络报文的传输过程中,任何人能够截获这个纸团,并层层剥开,但是如果没有相应的秘钥,是没法剥开TLS这一层的,也就没法看到里面的HTTP报文。
SSL和TLS的关系
SSL是网景制定的,TLS是IETF指定的。TLS1.0建立在SSL3.0基础之上,可以理解为SSL3.1。由于习惯原因,现在很多对安全层依旧沿用SSL的称呼,但实际上已经使用的是TLS的技术了。
网景是一家传说级的公司,Mozilla(火狐前身)和JavaScript都诞生于这家公司。
而IETF是一个开放的标准组织,网络上随处可见的RFC标准就是出自他们。
TLS 的运作过程
TLS的关键阶段
握手阶段是TLS的关键
握手过程,主要就是双方交换公共参数,生成对称密钥,用于加密通信
秘钥交换算法(key exchange):
- RSA based key exchange
- DH(Diffie-Hellman) based key exchange
交换秘钥过程
HTTPS常用的密钥交换算法有两种,分别是RSA
和ECDHE
由于RSA不具备向前安全性质,现在大部分服务器不会使用它来交换秘钥
每个通信过程的秘钥没有关系,相互独立,才能保证 「前向安全」。DH
作为ECDHE
的基础,详细介绍参考[DH]一节ECDHE
的演进路线基本是这样的:DH
-> DHE
->ECDHE
,
向前安全性
前向安全性或前向保密性(英语:Forward Secrecy,缩写:FS),有时也被称为完美前向安全(英语:Perfect Forward Secrecy,缩写:PFS),是密码学中通讯协议的安全属性,指的是长期使用的主密钥泄漏不会导致过去的会话密钥泄漏。
两种密钥交换模式
RSA Base
流程
从浏览器请求HTTPS,到渲染数据的整个过程
- Client Hello
- 随机数、算法套件
- Server Hello
- 随机数、选定算法、公钥
- Certificate
- 验证身份的项目:
- 涉及证书链的有效期
- 涉及证书链的签名
举例子,证书验证不通过的情况
- Server Hello Done
- Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message
- 服务端 Change Cipher Spec, Encrypted Handshake Message
- 加密通信
premaster key、masterKey的作用
DH Base
DH算法
DH算法的原理可以参考这一篇文章:为了搞懂 HTTPS,我把大学的数学书拿了出来。。。)
小结
要注意dh算法本身并不能防范中间人攻击,中间人可以通过分别对双方进行DH密钥交换进行攻击,所以DH实际需要配合身份认证机制才能安全工作。DH两种工作方式:
- Fixed Diffie-Hellman:直接把DH公钥固化到服务方证书里,和RSA一样不具备向前安全性
- Ephemeral Diffie-Hellman (简称为DHE):服务方使用动态DH公钥,只是会对公钥进行签名
在DH模式中,数字证书,不是用于生成通信用的秘钥,而是单纯用于双方认证身份(这个是大部分人很容易理解错的地方)
演进过程
以下过程基于网络交换秘钥场景
技术 | 性能 | 防监听(未篡改) | 防篡改 | 向前安全 |
---|---|---|---|---|
对称加密{原文} | √ | x | x | x |
非对称加密{原文} | x | √ | x | x |
非对称{密钥} | √ | √ | x | x |
非对称{密钥}+公钥认证 | √ | √ | √ | x |
临时DH公钥+公钥认证 | √ | √ | √ | √ |
TLS1.3介绍
TLS 1.3 是时隔九年对 TLS 1.2 等之前版本的新升级,也是迄今为止改动最大的一次。针对目前已知的安全威胁,IETF(Internet Engineering Task Force,互联网工程任务组) 正在制定 TLS 1.3 的新标准,使其有望成为有史以来最安全,但也最复杂的 TLS 协议。
![[Pasted image 20220112144654.png]]
v1.3 优点:
1.握手只需要 1TTR,性能更好
2.只支持向前安全的算法,安全性更高
实践
相关配置
- internet属性
如何查看一个网站使用的TLS版本:f12->security
证书主要生命周期
常见的证书管理工具有两种,一个是jdk自带的keytool;一个是开源项目openssl
以下命令均可以加上 –help 查看帮助,示例:keytool –genkeypair -help
阶段 | Keytool相关命令 | Openssl相关命令 |
---|---|---|
生成密钥对 | -genkeypair | genrsa |
生成证书请求 | -certreq | req |
签发证书 | -gencert | X509 |
管理密钥库 | -list, -importkeystore | pkcs12 -nodes -nocerts |
使用证书 | ||
吊销 |
keytool实践
# 生成根证书,存储到密钥库caroot.ks
keytool -genkeypair -alias caroot -keyalg RSA -keystore caroot.ks -storepass aaaaaa
# 根据提示,把密钥库转换成pkcs12
keytool -importkeystore -srckeystore caroot.ks -destkeystore caroot.p12 -deststoretype pkcs12
# 这里我们可以把keytool命令做个别名,方便调用(可选)
alias mykt='keytool -keystore $ksfile -storepass $kspass $*'
export ksfile=caroot.p12 && export kspass=aaaaaa
# 生成服务证书,存储到密钥库server.p12
keytool -genkeypair -alias server -keyalg rsa -keysize 1024 -keystore server.p12 -storepass aaaaaa -storetype pkcs12
# 生成服务证书签名申请
keytool -certreq -alias server -file server.csr -storepass aaaaaa -keystore server.p12
# 使用ca根证书签发服务证书
keytool -gencert -infile server.csr -outfile server.crt -alias caroot -keystore caroot.p12 -storepass aaaaaa -ext san=dns:www.mycompany.com,dns:mycompany.com
# 此时我们发现ca签发的证书不可信,导出根证书并安装到本地即可解决
keytool -exportcert -alias caroot -file caroot.crt -keystore caroot.p12 -storepass aaaaaa
验证der编码和base64编码的区别
der编码 base64编码
直接导出证书 导出证书 -rfc
windows下可以点击证书复制到文件
相互转换:notepad++转base64
要在nginx中开启TLS,需要先生成两个文件,一个是代表公钥的服务证书文件(.pem),一个是私钥的文件(.key)。此处公钥可以直接用windows的证书工具导出,私钥需要用openssl导出。
# 从密钥库中导出服务器私钥
openssl pkcs12 -in server.p12 -out server.key -nodes -nocerts
server {
#监听端口和域名
listen 443 ssl;
server_name localhost;
#以下两个为证书文件
ssl_certificate D:/java/nginx-1.14.2/cert/server-p.pem;
ssl_certificate_key D:/java/nginx-1.14.2/cert/server.key;
ssl_session_timeout 1m;
ssl_protocols SSLv2 SSLv3 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256:AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
ssl_prefer_server_ciphers on;
#location / {
#root D:/nginx/portal;
#index index.html;
#}
localtion / {
proxy_pass http://localhost:8899;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
# 访问80端口时转发到443端口,转为https访问
server {
listen 80;
server_name localhost;
return 301 https://$host$request_uri;
}
ERR_CERT_COMMON_NAME_INVALID错误
域名和Common Name 一致,chrome依然报错,该问题如何解决?
原因在于浏览器校验域名并不是从CN入手,而是检查x509的扩展字段SAN(subject alter name),所以只有加上该扩展字段,才能让浏览器认为网站是安全的。
针对keytool来说,有两个阶段可以加上该扩展字段:
- 生成密钥对时
- 签发证书时
扩展字段的指定方法参考keytool文档
如果是重新生成的证书未生效,需要清下浏览器缓存
chrome://net-internals/#hsts
若还是不行,看看是不是Nginx进程没清干净。
待办
- 什么时候会进行数字证书认证
- keytool -gencert -sigalg 有什么用
拓展
如何理解证书链
RSA vs DSA
这两个算法都是对称密钥算法,其中
- 名称问题
RSA三位发明者的名字的缩写
DSA则是 Digital Signature Algorithm 的缩写 - 数学基础
- RSA基于大数分解(两个素数的乘积)
- DSA基于离散对数难题
指纹和签名的区别
指纹只是证书上的散列。主要用于人工接收,检查证书是否为预定证书,比如 打电话给 CA认证机构 并说出指纹进行核对。 浏览器是通过签名来验证证书的有效性的,浏览器不会关注指纹。数字证书的数据结构
CN(Common Name名字与姓氏)
OU(Organization Unit组织单位名称)
O(Organization组织名称)
L(Locality城市或区域名称)
ST(State州或省份名称)
C(Country国家名称)
详细参考: https://www.cem.me/20150209-cert-binaries-4.html
- 头(4byte)
- body(4byte + len)
- signalg(2byte + len(13))
- sign(5byte+ len)
以sha256签名为例
计算公式:len(body) = 总大小 - 4 - (2+13) - (5 + 256)
body = cert[4:-276]
truststore和keystore的区别
keystore用于存放自己的证书和对应私钥,通常里面的证书作为TLS端的身份。
truststore用于存放自己这端信任的带签名的证书。
JKS、PKCS12都既可以做keystore也可以做truststore
CRL——证书吊销列表
证书超出有效期后会作废,用户也可以主动向 CA 申请撤销某证书文件,由于 CA 无法强制收回已经颁发出去的数字证书,因此为了实现证书的作废,往往还需要维护一个撤销证书列表(Certificate Revocation List,CRL),用于记录已经撤销的证书序号。
因此,通常情况下,当第三方对某个证书进行验证时,需要首先检查该证书是否在撤销列表中。如果存在,则该证书无法通过验证。如果不在,则继续进行后续的证书验证过程。
值得注意的是: 目前有些 CA 颁发的证书和大部分自签SSL证书都没有提供吊销列表 (CRL) 服务或证书 吊销列表分发点是不可访问的 ,固然更别提 OSCP 服务,这是很是危险的,由于若是证书丢失或被盗而没法吊销的话,就极有可能被用于非法用途而让用户蒙受损失。
秘钥、与ssh打通
域名及CName AAA、AAAA
PGP和GPG加签模式的区别
openssl验签流程
# 转换 DER 到 PEM 格式
$ openssl x509 -inform der -in root.cer -out root.pem
# 查看证书的签名,可以看到签名所使用的的 hash 算法是 sha256
$ openssl x509 -in root.pem -text -noout -certopt ca_default -certopt no_validity -certopt no_serial -certopt no_subject -certopt no_extensions -certopt no_signame
# 提取签名内容到文件中
$ openssl x509 -in root.pem -text -noout -certopt ca_default -certopt no_validity -certopt no_serial -certopt no_subject -certopt no_extensions -certopt no_signame | grep -v 'Signature Algorithm' | tr -d '[:space:]:' | xxd -r -p > root-signature.bin
# 提取根证书中含有的公钥
$ openssl x509 -in root.pem -noout -pubkey > root-pub.pem
# 使用公钥解密签名
$ openssl rsautl -verify -inkey root-pub.pem -in root-signature.bin -pubin > root-signature-decrypted.bin
# 查看解密后的内容
$ openssl asn1parse -inform DER -in root-signature-decrypted.bin
# 接下来我们计算证书的 hash 值
# 首先提取证书的 body
# 因为证书中含有签名,签名是不包含在 hash 值计算中的
# 所以不能简单地对整个证书文件进行 hash 运算
$ openssl asn1parse -in root.pem -strparse 4 -out root-body.bin &> /dev/null
# 计算 sha1 哈希值
$ openssl dgst -sha256 root-body.bin
#SHA1(root-body.bin)= xxx
# linux 下验证网站证书完整流程
# 新建一个文件夹 github 保存所有的文件
$ mkdir github && cd github
# 首先,我们下载 github.com 发送的证书
$ openssl s_client -connect github.com:443 -showcerts 2>/dev/null </dev/null | sed -n '/-----BEGIN/,/-----END/p' > github.com.crt
# github.com.crt 是 PEM 格式的文本文件
# 打开可以发现里面有两段 -----BEGIN CERTIFICATE----
# 这说明有两个证书,也就是 github.com 把中间证书也一并发过来了
# 接下来我们把两个证书提取出来
$ awk '/BEGIN/,/END/{ if(/BEGIN/){a++}; out="cert"a".tmpcrt"; print >out}' < github.com.crt && for cert in *.tmpcrt; do newname=$(openssl x509 -noout -subject -in $cert | sed -n 's/^.*CN=\(.*\)$/\1/; s/[ ,.*]/_/g; s/__/_/g; s/^_//g;p').pem; mv $cert $newname; done
# 我们得到了两个证书文件
# github_com.pem 和 DigiCert_SHA2_High_Assurance_Server_CA.pem
# 首先,验证 github_com.pem 证书确实
# 是由 DigiCert_SHA2_High_Assurance_Server_CA.pem 签发的
# 提取 DigiCert_SHA2_High_Assurance_Server_CA 的公钥
# 命名为 issuer-pub.pem
$ openssl x509 -in DigiCert_SHA2_High_Assurance_Server_CA.pem -noout -pubkey > issuer-pub.pem
# 查看 github_com.pem 的签名
# 可以看到 hash 算法是 sha256
$ openssl x509 -in github_com.pem -text -noout -certopt ca_default -certopt no_validity -certopt no_serial -certopt no_subject -certopt no_extensions -certopt no_signame
# 提取签名到文件中
$ openssl x509 -in github_com.pem -text -noout -certopt ca_default -certopt no_validity -certopt no_serial -certopt no_subject -certopt no_extensions -certopt no_signame | grep -v 'Signature Algorithm' | tr -d '[:space:]:' | xxd -r -p > github_com-signature.bin
# 使用上级证书的公钥解密签名
$ openssl rsautl -verify -inkey issuer-pub.pem -in github_com-signature.bin -pubin > github_com-signature-decrypted.bin
# 查看解密后的信息
$ openssl asn1parse -inform DER -in github_com-signature-decrypted.bin
# 接下来计算 github_com.pem 的 hash 值
# 提取证书的 body 部分
$ openssl asn1parse -in github_com.pem -strparse 4 -out github_com-body.bin &> /dev/null
# 计算 hash 值
$ openssl dgst -sha256 github_com-body.bin
# hash 值匹配,我们成功校验了 github.pem 这个证书确实是由 DigiCert_SHA2_High_Assurance_Server_CA.pem 这个证书来签发的。
上面的流程比较繁琐,其实也可以直接让 openssl 来帮我们验证。
$ openssl dgst -sha256 -verify issuer-pub.pem -signature github_com-signature.bin github_com-body.bin
# 获取上级证书的名字
$ openssl x509 -in DigiCert_SHA2_High_Assurance_Server_CA.pem -text -noout | grep Issuer:
Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
参考文献:
[序号]主要责任者.电子文献题名[电子文献及载体类型标识].电子文献的出版或获得地址,发表更新日期/引用日期.
例如:[12]王明亮.关于中国学术期刊标准化数据库系统工程的进展[EB/OL].1998-08-16/1998-10-01.
[8]万锦.中国大学学报文摘(1983-1993).英文版[DB/CD].北京:中国大百科全书出版社,1996.
[1]梁栋.java加密与解密的艺术(第二版)
[2]2018-2019年中国网络可信身份服务发展蓝皮书
[3]贾铁军.网络安全技术与应用(第三版)
[4]王绍斌.云计算安全事件:从入门到精通
[5]韩立刚.深入浅出计算机网络
[6]汪德嘉.身份危机
Https SSL/TLS PreMaster/Master Secret(Key)计算_服务器应用_Linux公社-Linux系统门户网站 (linuxidc.com)
https://cjting.me/2021/03/02/how-to-validate-tls-certificate/
如何将证书格式转换为PEM格式?_SSL证书管理 SCM_常见问题_其他_证书管理类_华为云 (huaweicloud.com)
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html
RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2 (rfc-editor.org)