参考:TLS/SSL 协议详解(6) SSL 数字证书的一些细节1 证书验证
地址:https://wonderful.blog.csdn.net/article/details/77867063
参考:TLS/SSL协议详解 (7) SSL 数字证书的一些细节2
目录
证书关系到了SSL的众多安全性,比如身份认证,密钥交换。所以有必要单拉出一章来讲证书。本章完善一下前几节中的身份认证的一些缺点。
首先,通过前面讲解,我们知道,证书需要几个重要的字段。例如"拥有者"、"颁发者"、"截止日期"。另外,生成证书的时候会自动生成一份"公钥"以及对应的"私钥"。现在假设我开了一个网站,准备用SSL加密的,需要让全世界信任我这个网站,那么我就需要一个证书,这时候我该怎么做?
证书生成
假设目前全世界就Google有那么一个root证书(根证书),名字叫做"Google",显然,它的"颁发者"也是"Google",因为根证书都是用工具生成的,并且根据第四章的知识,你应该知道,根证书没有颁发者,所以理所当然的,颁发者也是它自己。现在,全世界都信任这个证书,换句话说,浏览器里面都信任这个证书(约定俗成的)。我们也想和谷歌一样,有一个全世界都信任的证书,怎么做?
-
第一步:给Google钱。
-
第二步:告诉谷歌,我想要的证书名字叫做"www.dp.com"或者其他的你网站的域名。
-
第三步:等着谷歌返回给你:证书、公钥、私钥。
谷歌收到钱后是怎么做的呢?首先肯定对你的网站、公司进行调查,确保你不是坏人,以免全世界都信任你了你却干坏事,这会让谷歌丢脸。
-
第一步:用工具生成一对公钥和私钥。
-
第二步:构造证书,把公钥嵌入到证书里面(即证书里面其实有一个字段是公钥,明文的)
-
第三步:最重要的一步,谷歌拿自己的根证书签名一个第二步构造好的证书。
所谓签名:就是对第二步的证书做摘要,MD5或者SHA1,或者MD5+SHA1,得到结果,然后拿自己根证书的私钥加密这个结果,然后添加到证书最后面。
- 第四步:把证书和私钥交给你。
这就是一个证书的生成过程。后续将讲解为什么这么生成证书。
证书验证(如何保证你是证书的拥有者)
上一节我们花很多钱,让谷歌生用自己的证书为我们签名了一个证书。现在我们可以把谷歌给我们的证书,安装到我们的网站中了。如果有一个客户端,访问我们的网站,我们发送我们的证书给客户端,客户端如何验证呢?(注意,第四章的验证比较简单,这里才是真正的验证)
之前说过,客户端信任了许多根证书,比如客户端信任一个叫做"Google"的证书。首先浏览器收到我们的证书之后,查看它的颁发者,哦,是"Google",于是到信任的根证书里面找一个名字叫做"Google"的证书,通过字符串比较,我们很容易找到"Google"这个证书。然后进行验证:
-
第一步:对我们的证书(除了"签名值")全部内容进行MD5/SHA1,得到结果1。
-
第二步:用"Google"证书中的公钥,对我们证书中的"签名值"进行解密,得到结果2。
-
第三步:比较 结果1和结果2 ,发现一样,那么认证通过。理论上,如果中间没人改动我们的证书,那么,结果1和结果2 都是"asdfghjklqwertyu"。
证书是否能被伪造或盗用
那么上面的操作,如何保证了服务器身份能够被客户端认证呢?我们换个角度来思考,假设我是坏人,有一个网站叫做"www.dp.com",它的证书是Google签发的,我想欺骗客户端说自己是"www.dp.com",怎么办?
第一种方法:
我们想到,既然浏览器信任一个叫做"Google"的证书以及"www.dp.com",那我就在我的网站导入"www.dp.com"它不就行了吗,因为证书都是公开的,我随时随地能获得"www.dp.com"的证书?
但是根据第四章中"密钥的协商、交换"一节中提到的那样,公钥和私钥是一对的,我虽然得到了"www.dp.com"证书,证书中也有"www.dp.com"的公钥,但是我没有它对应私钥,而SSL的RSA算法握手时需要私钥操作。
比如,我虽然把"www.dp.com"证书发给客户端,客户端的确认证了,然后客户端用"www.dp.com"证书中的公钥加密一个密钥,按握手要求,服务器需要用私钥来解密,可是我没有对应私钥,以没办法解密!后面的会话都解密不了,即SSL握手完成不了。
对于ECDHE算法来说和RSA有点区别,由于私钥不参与秘钥协商(前向安全算法都不需要私钥进行秘钥协商),所以为了保证服务器是证书的拥有者(即拥有私钥),SSL协议规定,服务器在握手时,需要用私钥签名握手数据(server key exchange),客户端需要用公钥验证这个签名的握手数据。可见,如果服务器没有私钥,那么也就不能完成签名这步,或者签名的值客户端无法验证,握手无法继续。
第二种方法:
那我就也用工具,随便生成一个根证书,名字叫做"Google",对应该证书,也会随机生成一个公钥和私钥,然后模仿谷歌的,拿这个根证书的私钥签名一个证书:颁发者填写"Google",拥有者填写"www.dp.com" ,然后做MD5/SHA1,得到结果"asdfghjklqwertyu",拿刚才自己生成的"Google"证书的私钥,对这个结果加密,得到"lkjhgfd":
我把这个证书发给客户端,客户端找到叫做"Google"的证书,拿它的公钥解密这个签名值,问题来了,这个由于这个签名值是由我刚才假冒的"Google"证书的私钥签名的,所以,正宗的"Google"正宗的公钥解密得到的结果,压根不是"asdfghjklqwertyu",当客户端对我们这个证书做摘要,结果是"asdfghjklqwertyu",但是对签名解密的结果却是另外一个值,那么浏览器就不信任你了。
第三种方法:
看来生成证书的方法行不通,那只能随便找一个证书,修改里面的颁发者和拥有者。
但是问题又出现了,被修改证书的签名值没办法更改,因为签名值是拿其颁发者私钥进行加密的,我们没有颁发者Google的私钥。浏览器会对被篡改证书的签名用Google的公钥解密,然后对比浏览器自己对证书计算的摘要,不一样,浏览器就不认了,如果我们要改签名值,就需要颁发者即"Google"的私钥,不过这显然不可能。
上面伪造身份的结果的是失败的,其安全性都是基于那个"签名值",签名值是用颁发者自己的私钥加密的,只要颁发者的私钥不泄密,那么不可能有人伪造证书。
证书链
之所以存在证书链这个东西,是因为验证证书的时候需要。
假设如上图所示,"Google"签名了一个"Android",然后"Android"签名了一个"CyanogenMod",我们浏览器只信任"Google",如果服务器只发送一个叫做"CyanogenMod",那么我们无法查找到叫做"Android"的根证书,无法验证服务器的身份。此时,作为服务器,我们可发送"Android"+"CyanogenMod",这样,客户端会首先验证Android是否是CyanogenMod的颁发者(验证签名),如果是,那么在自己的根证书里面找"Android"的颁发者"Google",然后验证"Android"是否是由"Google"颁发的。
证书格式
编码格式
证书编码格式多种,但是不要根据文件后缀名(der,cer)等区分证书格式。
总的来说,证书分为2种,一种是二进制的、一种是进行base64编码的证书。前者使用notepad或者任意文本编辑器打开,显示乱码,后者则显示正常的base64编码后的数据。下图为经过base64编码后的证书,由BEGAIN和END包括。(老司机可能就发现了,有点像长了一点的迅雷下载链接)
至于是否换行完全取决于base64的规范(详见wiki中关于base64 https://en.wikipedia.org/wiki/Base64)。
所谓二进制证书,也就是原始的asn1格式的证书,如果熟悉asn1编码方式,直接看2进制会看到明显的'30 82 ...'等asn1的类型长度标识,这里不再赘述,但是二进制不适合网络传输,所以普遍采用base64将其编码。
其次还有一种格式叫pfx(PKCS12)格式的证书,与其说是证书,不如叫它证书+私钥的package比较合适,一个文件即包含证书(证书链),也包含私钥。pfx本身可被加密,所以可能需要输入密钥才能解析pfx。所以,解析pfx或者打开pfx时,可能需要用户输入2个密码,一个是pfx本身的密码,另一个是私钥的密码(私钥见下文)。
另一种证书格式称之为p7b,他是多个证书组织成的格式(一般是证书链)。在windows下可以由windows自带程序解析,我们可以提取出其中各个证书。
私钥格式
私钥格式也分为 二进制 和 base64编码,不再赘述。
但是私钥本身可以被加密。
被加密的私钥格式如下:
当采用pfx格式证书时,由于pfx格式文件本身可能需要密钥来解密,而里面的私钥也可能需要密钥解密,所以解析程序往往可能让你输入2个密钥,这2个密钥是用来解密不同层级数据的,注意不要感到疑惑或者将两者混淆。
证书类型
签名算法一般采用RSA或者ECC。较老的有DH算法等,目前已不多见。
但是注意,被称为RSA证书并不是指证书是被RSA算法签名的,而是指证书本身的公钥、私钥是RSA。同理ECC证书指的是证书的公钥和私钥具有椭圆曲线属性。证书的签名值的类型并不影响证书的属性。
例如,上级证书A是ECC证书,即证书公钥私钥是ECC属性的,那么由它生产的证书B的签名必然采用ECC签名,但是B本身可以使用RSA公钥私钥或者ECC公钥私钥。
证书拓展
使用wireshark解析SSL证书,我们可以清晰的看到数字证书各个字段,这里我们关心证书中的extension:
1:keyusage/extkeyusage
用以描述证书的用法,改证书可以进行证书的签发?CRL的签发?客户端认证?服务器认证?一般严格的CA机构都谨慎设置这个字段,避免自己签发的证书被滥用。
2:subectkeyidentifier
自己公钥进行hash运算后的值,可以快速判断证书。
3:authoritykeyidentifier
上级证书的公钥进行hash运算后的值。一般来说,两个上下级关系的证书,下级证书的authoritykeyidentifier值就是上级证书的subectkeyidentifier值。
4:subjectAltname
证书的别名。例如一个网站有多个域名,例如www.baidu.com和www.hao123.com对应的都是一个服务器,common name只能写一个,为了不让浏览器告警,可以在subjectAltname拓展中添加这个网站的其他域名。浏览器收到这个证书,除了判断host和common name是否一致外,也会判断host和subjectAltname是否有一致项,有的话就成功。
5:basicConstraints
一般CA证书里面ca:Ture。