农妇山泉一亩田 发表于 2024-7-31 09:49:24

深入分析SSL/TLS服务器的证书(C/C++代码实现)

SSL(Secure Sockets Layer)和TLS(Transport Layer Security)是网络安全领域的告急协议,它们在掩护网络通信中发挥着至关告急的作用。这些协议通过加密和身份验证机制,确保数据在传输过程中的秘密性和完整性,防止数据被窃取或篡改。
这些协议的关键构成部分之一是SSL/TLS服务器的证书,它为服务器提供了一种身份验证和加密的方式
详细解释SSL/TLS的告急性

以下将详细解释SSL/TLS的告急性:

[*]加密掩护:SSL/TLS协议使用对称密钥和非对称密钥技术对数据举行加密,确保只有目的吸收者能够解密并阅读信息。这种加密掩护使得敏感信息如信用卡详情、医疗纪录和个人身份信息在互联网传输时得到掩护。
[*]身份验证:SSL/TLS通过证书的使用提供了身份验证机制。服务器必须出示由可信证书颁发机构签发的证书,客户端可以验证服务器的真实性,以防止中心人攻击。
[*]数据完整性:这些协议确保数据在传输过程中不被篡改。任何对传输数据的未授权修改都会被检测出来,从而包管了数据的完整性。
证书的基础结构

SSL/TLS服务器的证书是基于公钥基础设施构建的,其中包罗以下几个关键部分:


[*]主题:证书的主题指定了证书持有者的详细信息,包罗构造名称、城市、国家代码等。
[*]颁发者:颁发者是签发证书的证书颁发机构。
[*]公钥:证书包罗一个公钥,用于加密信息或验证数字签名。
[*]签名算法:此部分指明了CA用来签订证书的加密算法。
[*]有用期:证书的有用期起始日期和竣事日期,指明了证书的有用限期。
证书链分析是理解SSL/TLS安全性的关键环节

在网络安全中,SSL和TLS协议通过加密保障数据传输的安全性,而这一过程的核心是证书链的验证。

[*] 证书链的基本概念

[*]证书链定义:证书链是指由多个证书构成的信托链,从根证书(Root Certificate)开始,通过一级或多级中心证书(Intermediate Certificates),最终到达服务器证书(Server Certificate)。
[*]根证书的作用:根证书是证书链的出发点,由可信的证书颁发机构(CA)签发。根证书的公钥广泛预装在各种操作体系和浏览器中,因此通常被无条件信托。
[*]中心证书的作用:中心证书作为根证书与用户证书之间的中心环节,起到传递信托的作用。它们由根证书签发,并依次签发下级证书,形成完整的证书链。
[*]服务器证书的作用:服务器证书是证书链的末端,直接用于SSL/TLS通信中的身份验证和加密。服务器证书由中心或根证书签发,证明服务器的真实身份。

[*] 证书链的验证过程

[*]证书路径构建:客户端吸收到服务器证书后,会查抄证书中指定的颁发者信息,并尝试构建从服务器证书到可信托根证书的完整路径。
[*]单向验证与双向验证:在某些情况下,只需验证服务器证书是否由可信CA签发(单向验证)。在其他情况下,还需验证CA的合法性,即从服务器证书到根证书的每一级证书都需要举行验证(双向验证)。
[*]证书吊销状态查抄:客户端可以通过查询证书吊销列表(CRL)或在线证书状态协议(OCSP)来查抄证书是否被吊销,确包管书的有用性。
[*]签名验证:每个证书都包罗上一级证书的签名。客户端会使用上一级证书的公钥验证该签名的合法性,从而确保整个证书链的完整性和真实性。

[*] 证书链的告急性

[*]防止中心人攻击:通过完整的证书链验证,客户端可以确保自己与真正的服务器举行通信,而非被中心人冒充的假服务器。
[*]加强用户信托:当浏览器能够乐成验证证书链并表现安全锁标记时,用户对网站的信托度会提高,这在电子商务和敏感信息传输中尤为告急。

[*] 证书链的潜在问题与办理方案

[*]路径验证失败:假如客户端无法构建有用的证书路径或某个证书无法通过验证,会导致路径验证失败。这通常会引起浏览器的安全告诫,提示用户证书不可信。
[*]中心证书缺失:假如服务器没有提供完整的证书链,而是仅提供了服务器证书,部分客户端可能无法自动获取和验证中心证书,造成验证失败。办理这一问题的方法是在服务器配置中包罗完整的证书链。
[*]根证书不信托:假如客户端不信托根证书,整个证书链也会被认为是不可信的。因此,选择广泛信托的CA颁发的证书至关告急。

[*] 现实案例分析

[*]案例一:Let’s Encrypt:Let’s Encrypt是一个广泛使用的免费CA,其证书链包罗一个根证书和一个中心证书。由于其根证书已被大多数当代浏览器预信托,因此在使用时通常不会出现验证问题。
[*]案例二:自签名证书:一些网站可能会选择使用自签名证书来节流本钱,但由于这些证书不是由可信CA签发,因此在公共网络上险些总是会引发浏览器的安全告诫。
[*]案例三:过期中心证书:假如服务器的中心证书过期,即使根证书和服务器证书仍然有用,也会导致整个证书链验证失败。办理这个问题需要更新中心证书或重新天生完整的证书链。

什么是签名算法

签名算法是一种用于验证数据完整性、数据泉源和抗否认的技术。在密码学中,签名算法是利用非对称加密的原理,通过私钥举行签名,而公钥用于验证签名的合法性,从而确保消息的真实性和安全性。
签名算法的核心作用在于提供一个安全的方法来验证信息确实由特定的发送方发出,并且内容自签名后未被篡改。这一过程通常涉及到对原始信息举行哈希处理惩罚,然后使用发送方的私钥对哈希值举行加密,形成数字签名。吸收方收到信息后,可以使用发送方的公钥对签名举行解密验证。
对于常用的签名算法,告急包罗RSA、DSA和ECDSA三种。RSA是一种非常经典的算法,广泛用于SSL证书、代码签名等场景。DSA则专门用于数字签名,不能用于加密息争密,其长处是速度较快。ECDSA则是基于椭圆曲线密码学的签名算法,相对于RSA和DSA,其具有更高的安全性和更快的处理惩罚速度。
在现实应用中,数字签名常常应用于软件发布、文件传输、在线生意业务等领域,以确保信息的完整性和真实性。比方,下载软件时,开发者通常会提供MD5或SHA256散列值,用户可以通过验证这些散列值来确保所下载的文件未被篡改。同样,在线付出过程中,通过数字签名验证可以确保生意业务信息的安全和真实。
别的,选择得当的签名算法也至关告急。RSA算法因其汗青久长和广泛的支持而被普遍接受,但ECDSA由于其更高的安全性和服从正逐渐受到重视。在选择算法时,除了考虑安全性外,还需考虑性能、兼容性和实现的便利性等因素。
SSL/TLS证书有用期

证书有用期是SSL/TLS证书有用性的时间范围,通常为13个月。 SSL(Secure Sockets Layer)和TLS(Transport Layer Security)证书用于在互联网上掩护数据的安全传输,它们通过加密和身份验证机制防止数据被窃取或篡改。证书的有用期是其告急属性之一,决定了证书能够在多长时间内提供安全保障。以下将详细分析证书有用期的各个方面:

[*]证书有用期的汗青变化

[*]早期规定:在2011年之前,SSL/TLS证书的有用期通常为8-10年。随着时间的推移,为了提高安全性,有用期逐渐收缩。
[*]逐步收缩:2011年,CA/B论坛将证书有用期收缩至5年。2015年,有用期进一步收缩至3年。2018年,有用期再次收缩为2年。
[*]最新规定:从2020年9月1日起,主流浏览器厂商如苹果、谷歌和火狐推动了一项新政策,要求SSL/TLS证书的最长有用期不得超过13个月(398天)。

[*]为什么证书有用期要收缩

[*]安全性提升:收缩证书有用期可以更频仍地更新加密算法和密钥,降低被破解的风险。这有助于应对快速发展的网络安全威胁。
[*]淘汰旧证书数目:较短的有用期迫使网站管理员定期更新证书,从而淘汰了被遗忘或被忽视的旧证书数目,提高了整体网络安全性。
[*]快速响应毛病:一旦发现加密算法或证书管理方面的毛病,短期证书可以更快地推动修复和更新,确保体系安全。

[*]证书有用期对企业的影响

[*]安全管理优势:短期证书能够促使企业保持对安全步伐的关注和更新,从而使用最新的加密技术和标准。
[*]管理负担加重:需要每年更新证书会增长IT管理人员的工作负担和管理本钱。对于拥有大量服务器和证书的企业来说,这一挑战尤为明显。
[*]经济本钱增长:频仍更新证书可能会增长企业的运营本钱,包罗人工费、管理费等。

[*]怎样查看和管理证书有用期

[*]在浏览器中查看:访问网站时,点击地点栏的安全锁标记,可以查看SSL证书的有用期。
[*]使用工具查看:利用各种在线工具,只需输入域名即可检测到SSL证书的详细信息,包罗有用期。
[*]自动化管理:对于大量证书的管理,建议采用自动化工具举行监控和更新,以避免因疏忽导致证书过期。

[*]证书有用期过期的现实案例

[*]特斯拉APP宕机:2020年,特斯拉APP因SSL证书过期导致大面积宕机,用户无法正常使用手机钥匙功能。
[*]美国当局网站故障:2018年,美国国家航空航天局、司法部等80多个当局网站因SSL证书过期出现无法访问的情况。
[*]爱立信手机服务中断:同年,爱立信手机服务因数字证书过期,导致数百万用户无法发短信或打电话。

颁发者可信度

颁发者可信度是评估SSL/TLS证书安全性的告急指标之一。在网络安全中,SSL和TLS证书通过加密和身份验证机制掩护数据传输的安全性。而证书的颁发者,即证书颁发机构(CA),在信托链中扮演着至关告急的角色。以下将详细分析颁发者可信度的各个方面:

[*]CA的信托基础

[*]法律授权:CA的合法性通常基于国家法律或国际认可,确保其具备合法的资格签发受信托的证书。
[*]技术本事:CA需要具备强大的加密技术和管理本事,以包管证书的天生、分发和吊销过程的安全性和可靠性。
[*]中立性:CA必须保持严酷的中立性,不受任何第三方影响,公正地处理惩罚证书相关事务。

[*]CA的验证流程

[*]域名验证:对于域验证(DV)证书,CA只需验证申请者是否控制了相应的域名。
[*]构造验证:对于构造验证(OV)证书,CA会进一步验证构造的身份和合法性,确保申请者是合法实体。
[*]扩展验证:扩展验证(EV)证书要求最严酷,包罗详尽的背景观察和法律文件考核,确保申请者的合法身份和业务真实性。

[*]CA的公信力

[*]市场认可度:知名CA如VeriSign、DigiCert、GlobalSign等因其长期的良好表现和广泛预装在各种装备上的根证书而享有较高声誉。
[*]用户反馈:用户对CA服务的评价也会影响其公信力,包罗响应速度、技术支持和问题办理本事。
[*]透明度:CA的透明度越高,用户越能相识其运营和策略,从而加强信托感。

[*]CA的安全纪录

[*]汗青安全事件:CA汗青上是否发生过重大安全事件,如未经授权的证书签发,会对可信度产生重大影响。
[*]毛病响应:CA面临发现的毛病和安全威胁时,其响应速度和处理惩罚方式也是衡量可信度的告急标准。
[*]连续监控:CA是否拥有连续的安全监控和改进机制,以确保长期保持高水平的安全性。

[*]颁发者可信度的现实案例

[*]DigiNotar事件:2011年,荷兰CA DigiNotar遭受黑客攻击,导致其信托基础完全崩溃,最终破产。
[*]Symantec的公信力问题:2018年,Symantec因未能妥善管理其PKI基础设施而被浏览器厂商逐步不信托其旧证书,损害了其公信力。
[*]Let’s Encrypt的崛起:作为一个免费、自动化和开放的CA,Let’s Encrypt以其创新和透明性迅速获得了广泛的市场认可。

公钥强度

公钥强度是衡量非对称加密算法安全性的关键指标,它决定了密码破解的难易水平和计算复杂度。在密码学中,公钥和私钥成对出现,公钥用于加密数据,而私钥则用于解密。已知私钥可以推导出公钥,但公钥不能反推私钥,从而确保了加密信息的安全性。以下将详细分析公钥强度的各个方面:

[*]密钥长度与强度

[*]RSA算法:RSA算法是最广泛使用的非对称加密算法之一,其强度告急取决于密钥长度。现在,RSA 1024位密钥已不再安全,逐渐被2048位及以上的密钥替换。RSA 2048位密钥可提供约112位的安全强度,而3072位密钥提供128位安全强度。
[*]ECC算法:相比于RSA,椭圆曲线密码(ECC)算法具有更高的单位比特安全强度。比方,256位的ECC密钥强度已经比2048位的RSA密钥强度要高。
[*]SM2算法:作为国产密码标准,SM2算法也是基于椭圆曲线的,其256位密钥即可达到较高的安全强度。

[*]密码算法与强度

[*]对称式加密算法:这类算法的密钥长度直接决定了穷举攻击的复杂度,比方AES算法的密钥长度即代表其安全强度。
[*]非对称式加密算法:RSA和ECC等算法的密钥长度不仅影响加密速度,还决定破解难度。长密钥意味着更高的安全强度。
[*]Hash算法:如SHA-2系列算法,其强度体现在抗碰撞和雪崩效应上。

[*]国际与国产算法

[*]RSA与SM2:RSA算法由美国NSA提出,密钥长度不绝增长以应对安全威胁;SM2则是中国自主研发的,基于椭圆曲线,相对于RSA在同等安全强度下运算速度更快。
[*]ECC与SM2:SM2与国际上的ECC算法相对应,其签名、密钥交换和公钥加密功能分别对应于ECC算法的ECDSA、ECDH、ECIES。

[*]不同应用场景的密钥要求

[*]金融级应用:根据国际建议,金融级应用的加密算法密钥强度应达到112位,长期掩护则需128位或更高。
[*]国家安全应用:美国NSA对SECRET级别信息的密钥强度要求为128位,TOP SECRET级别则需192位。

[*]密码算法发展趋势

[*]对称密码算法:如AES算法,密钥长度将逐渐从128位增长到192位、256位。
[*]非对称密码算法:ECC算法将逐渐占据告急地位,密钥长度从224位增长;RSA算法密钥长度也在增长。
[*]轻量级密码:实用于资源受限环境,如物联网装备。这些算法包罗DESL、HIGHT等对称密码,及NTRU、BlueJay等非对称密码。

[*]国密算法与安全性

[*]SM2:基于椭圆曲线,设计时考虑多种攻击手段,具有较高的安全性。
[*]SM3:输出长度为256比特的散列算法,抗碰撞本事强,设计上考虑了雪崩效应。
[*]SM4:分组密码算法,128比特密钥和分组长度,颠末多轮安全性评估,尚未有公开的有用攻击方法。

证书扩展

证书扩展是数字证书中的一个告急构成部分,它提供了一种机制,允许在证书中包罗额外的属性和信息,以加强证书的功能性和灵活性。
自签名证书可以举行私有扩展,这对于内部使用或测试环境非常有用。标准扩展则实用于由CA机构签发的证书,这些扩展项包罗SAN(Subject Alternative Name)、密钥标识符、密钥用法和基本束缚等,它们大大增长了证书的使用场景和灵活性。特殊是SAN扩展,它允许一张证书绑定多个域名、IP地点或电子邮件地点,这对多域名支持尤其告急。
X.509标准定义了公钥证书的字段和扩展名,其中版本3引入了对证书扩展的支持,这为证书提供了更多属性与公钥关联的方法。比方,subjectAltName扩展可以包罗电子邮件地点、域名、IP地点等,并且这些值可以有多个,这在现实应用中非常告急。
当一个网站使用TLS证书举行身份标记时,假如证书中包罗subjectAltName,体系将优先使用这些信息来识别证书持有者,而不是依赖于Subject子项。这种机制在当代网站的TLS实现中非常常见,以确保网站身份的正确识别。
证书吊销列表(CRL)和在线证书状态协议(OCSP)

证书吊销列表(CRL)和在线证书状态协议(OCSP)都是用于验证数字证书有用性的告急机制,它们各自以不同的方式资助维护网络安全和信托。
CRL是一种由证书颁发机构(CA)定期发布的文件,其中列出了所有已吊销的证书序列号及其吊销日期。这种机制允许网站管理员和浏览器查抄特定证书是否已被吊销。CRL的访问方式通常是通过下载最新的CRL文件,并与本地证书举行比对来确认其有用性。这种方法是静态的,意味着它需要定期手动更新CRL文件,以确保信息的最新性。
相比之下,OCSP提供了一种动态的证书状态查抄方法。通过实时向CA发送请求,OCSP能够即时返回证书的状态,包罗“正常”、“吊销”或“未知”。这种即时性使得OCSP在需要快速验证证书状态的场景中特殊有用,如在线事务处理惩罚时。
CRL的更新频率通常较低,这可能导致其信息可能不是最即时的。更新的频率取决于CA的政策,可能是每天一次或每月一次。因此,尽管CRL提供了一种可行的证书状态查抄方式,但它不能提供实时的证书状态信息。
而OCSP恰好弥补了CRL的这一不敷,它通过实时查询,能够提供即时的证书状态信息。当用户需要确保生意业务时对方证书的即时有用性时,OCSP提供了一个可靠的办理方案。然而,OCSP的响应时间受到服务器性能和网络耽误的影响,可能在高并发场景下影响性能。
从应用场景来看,CRL更适合于不频仍变动证书的场景,或者在对实时性要求不高的环境中使用。比方,一些企业内部体系可能会选择CRL作为验证员工证书状态的方式。相反,对于那些要求高安全性和即时性的应用场景,如电子商务平台,更倾向于使用OCSP来确保每次生意业务的安全性。
在选择使用CRL还是OCSP时,还应考虑体系的性能和网络环境。CRL由于需要定期下载,可能会增长网络负担,特殊是在网络条件受限的环境下。而OCSP固然提供了实时的验证,但需要维护稳定的网络连接到OCSP服务器,并且其响应速度必须富足快以支持大量的实时查询。
使用OpenSSL工具分析SSL/TLS证书

使用OpenSSL工具分析SSL/TLS证书可以提供证书的详细信息,包罗其结构、内容、有用性等。以下是一些常用的OpenSSL下令示例,用于分析证书:
1. 表现证书信息
要查看证书的详细信息,可以使用以下下令:
openssl x509 -in certificate.crt -text -noout
这个下令将表现证书的文本表示情势,包罗版本、序列号、签名算法、有用期、主题信息、颁发者信息等。
2. 查抄证书链
要验证证书链,可以使用-untrusted选项和中心CA证书或链中的其他证书:
openssl verify -CAfile intermediate.crt certificate.crt
假如证书链有用,下令将表现类似certificate.crt: OK的消息。
3. 查抄证书有用期
要快速查看证书的有用期,可以使用:
openssl x509 -in certificate.crt -dates -noout
这将输出证书的"notBefore"和"notAfter"日期。
4. 提取证书的公钥
要提取并表现证书的公钥,可以使用:
openssl x509 -in certificate.crt -pubkey -noout
这将表现证书公钥的详细信息。
5. 验证证书签名
要验证证书的签名,可以使用:
openssl x509 -in certificate.crt -check_ss_sig
假如证书是由其颁发者正确签名的,下令将表现签名验证乐成的消息。
6. 查抄证书是否被吊销
要查抄证书是否被吊销,可以使用CRL或OCSP。对于CRL,可以使用:
openssl crl -in crl_number.crl -noout -text
对于OCSP,可以使用:
openssl ocsp -issuer issuer_certificate.pem -cert certificate_to_check.crt -url http://ocsp.certificateAuthority.com
这些下令将查抄证书是否在CRL中或OCSP响应中被标记为吊销。
7. 表现证书的指纹
要表现证书的指纹,可以使用:
openssl x509 -in certificate.crt -noout -fingerprint -sha256
这将输出证书的SHA-256指纹,可用于比力和验证。
8. 导出证书为DER格式
假如需要将证书导出为DER格式,可以使用:
openssl x509 -outform der -in certificate.crt -out certificate.der
这将证书从PEM格式转换为DER格式。
9. 从证书中提取证书策略
要查看证书的CPS(证书策略)或任何其他扩展,可以使用:
openssl x509 -in certificate.crt -extfile <(echo "[ v3_ca ]") -extensions v3_ca -noout -text
这将表现证书中的自定义扩展信息。
10. 表现证书的主题备用名称(SAN)

要查看证书的SAN扩展,可以使用:
openssl x509 -in certificate.crt -text -noout
-subject_alt_name 这将列出证书支持的所有备用域名或IP地点。
这些下令提供了对SSL/TLS证书举行深入分析的基础,可以资助你相识证书的属性、验证其安全性和信托链,以及确保它实用于你的安全需求。
...

int main (int argc, char **argv)
{

...
        static struct option long_options[] = {
                { "bits", no_argument, 0, 'b' },
                { "chain", no_argument, 0, 'c' },
                { "cipher", no_argument, 0, 'C' },
                { "issuer", no_argument, 0, 'i' },
                { "method", no_argument, 0, 'm' },
                { "no-sni", no_argument, 0, 'N' },
                { "quiet", no_argument, 0, 'q' },
                { "raw", no_argument, 0, 'r' },
                { "serial", no_argument, 0, 'S' },
                { "signature-algorithm", no_argument, 0, 'A' },
                { "subject", no_argument, 0, 's' },
                { "validity", no_argument, 0, 'V' },
                { "help", no_argument, 0, 'h' },
                { "version", no_argument, 0, 'v' },
        };

        while ((opt_value = getopt_long(argc, argv, "bcCimNqrSAsVhv", long_options, &long_opt_index)) != -1) {
                switch (opt_value) {

                        case 'b':
                                bits = 1;
                                continue;

                        case 'c':
                                chain = 1;
                                continue;

                        case 'C':
                                cipher = 1;
                                continue;

                        case 'i':
                                issuer = 1;
                                continue;

                        case 'm':
                                method = 1;
                                continue;

                        case 'N':
                                no_sni = 1;
                                continue;

                        case 'q':
                                quiet = 1;
                                pad_fmt = 0;
                                continue;

                        case 'r':
                                raw = 1;
                                continue;

                        case 'S':
                                serial = 1;
                                continue;

                        case 'A':
                                sig_algo = 1;
                                continue;

                        case 's':
                                subject = 1;
                                continue;

                        case 'V':
                                validity = 1;
                                continue;

                        case 'h':
                                usage();
                                exit(EXIT_SUCCESS);

                        case 'v':
                                fprintf(stdout, "%s\n", KEUKA_VERSION);
                                exit(EXIT_SUCCESS);
                        case '?':
                                usage();
                                exit(EXIT_FAILURE);
                        default:
                                break;
                }
        }
...

        /**
       * 限制作为参数给定的主机名的长度
       */
        if (length(argv) > MAX_HOSTNAME_LENGTH) {
                fprintf(stderr, "Error: Hostname exceeds maximum length of 256 characters.\n");
                exit(EXIT_FAILURE);
        }

        /**
       * argv中的最后一个元素应该是对等主机名
       */
        hostname = argv;

        /**
       * 组装请求的URL
       */
        copy(url, protocol);
        concat(url, "://");
        concat(url, hostname);

        /**
       * 运行OpenSSL初始化任务
       */
        SSL_load_error_strings();
        ERR_load_crypto_strings();
        OpenSSL_add_all_algorithms();
        OpenSSL_add_all_digests();
        SSL_library_init();

        /**
       * Start execution clock.
       */
        start = clock();

        /**
       * 初始化新的BIO.
       */
        bp = BIO_new_fp(stdout, BIO_NOCLOSE);


        ssl_method = SSLv23_client_method();

        if (!quiet) {
                BIO_printf(
                        bp,
                        "%s [%fs] Establishing SSL context.\n",
                        KEUKA_NEUTRAL_INDICATOR,
                        get_elapsed_ticks(start)
                );
        }

        /**
       * 建立新的SSL上下文
       */
        if (is_null(ctx = SSL_CTX_new(ssl_method))) {
                /**
               * 以进度格式显示错误,除非给出--quiet。
               */
                if (!quiet) {
                        BIO_printf(
                                bp,
                                "%s [%fs] Error: Unable to establish SSL context.\n",
                                KEUKA_NEUTRAL_INDICATOR,
                                get_elapsed_ticks(start)
                        );
                } else {
                        BIO_printf(bp, "Error: Unable to establish SSL context.\n");
                }

                goto on_error;
        }

        if (!quiet) {
                BIO_printf(
                        bp,
                        "%s [%fs] SSL context established.\n",
                        KEUKA_NEUTRAL_INDICATOR,
                        get_elapsed_ticks(start)
                );
        }

        /**
       * 建立TCP套接字连接
       */
        server = mksock(url, bp);

        if (!quiet) {
                BIO_printf(
                        bp,
                        "%s [%fs] Establishing connection to %s.\n",
                        KEUKA_OUTBOUND_INDICATOR,
                        get_elapsed_ticks(start),
                        hostname
                );
        }

        /**
       * 如果建立连接时出现问题,请退出。
       */
        if (is_error(server, -1)) {

                if (!quiet) {
                        BIO_printf(
                                bp,
                                "%s [%fs] Error: Unable to resolve hostname %s.\n",
                                KEUKA_INBOUND_INDICATOR,
                                get_elapsed_ticks(start),
                                hostname
                        );
                } else {
                        BIO_printf(bp, "Error: Unable to resolve hostname %s.\n", hostname);
                }

                exit(EXIT_FAILURE);
        }

        if (!quiet) {
                BIO_printf(
                        bp,
                        "%s [%fs] Connection established.\n",
                        KEUKA_INBOUND_INDICATOR,
                        get_elapsed_ticks(start)
                );
        }

        /**
       * 建立连接,在客户端模式下设置状态。
       */
        ssl = SSL_new(ctx);
        SSL_set_connect_state(ssl);

        /**
       * Disable SNI support if --no-sni was given.
       */
        if (!no_sni) {
                SSL_set_tlsext_host_name(ssl, hostname);
        }

        if (!quiet) {
                BIO_printf(
                        bp,
                        "%s [%fs] Attaching SSL session to socket.\n",
                        KEUKA_NEUTRAL_INDICATOR,
                        get_elapsed_ticks(start)
                );
        }

        attach = SSL_set_fd(ssl, server);

        /**
       * 将SSL会话连接到TCP套接字。
       */
        if (is_error(attach, -1)) {

                if (!quiet) {
                        BIO_printf(
                                bp,
                                "%s [%fs] Error: Unable to attach SSL session to socket.\n",
                                KEUKA_NEUTRAL_INDICATOR,
                                get_elapsed_ticks(start)
                        );
                } else {
                        BIO_printf(bp, "Error: Unable to attach SSL session to socket.\n");
                }

                goto on_error;
        }

        if (!quiet) {
                BIO_printf(
                        bp,
                        "%s [%fs] SSL session attached, handshake initiated.\n",
                        KEUKA_OUTBOUND_INDICATOR,
                        get_elapsed_ticks(start)
                );
        }

        status = SSL_connect(ssl);


        if (status != 1) {

                if (!quiet) {
                        BIO_printf(
                                bp,
                                "%s [%fs] Error: Could not build SSL session with %s. Handshake aborted.\n",
                                KEUKA_NEUTRAL_INDICATOR,
                                get_elapsed_ticks(start),
                                url
                        );
                } else {
                        BIO_printf(
                                bp,
                                "Error: Could not build SSL session with %s. Handshake aborted.\n",
                                url
                        );
                }

                goto on_error;
        }

        ssl_cipher = SSL_get_current_cipher(ssl);

        if (!quiet) {
                BIO_printf(
                        bp,
                        "%s [%fs] %s negotiated, handshake complete.\n",
                        KEUKA_INBOUND_INDICATOR,
                        get_elapsed_ticks(start),
                        SSL_CIPHER_get_version(ssl_cipher)
                );

                if (pad_fmt) {
                        BIO_printf(bp, "\n");
                }
        }

        /**
       * 如果给定了--cipher,则打印使用的密码。
       */
        if (cipher) {
                BIO_printf(
                        bp,
                        "--- Cipher: %s\n",
                        SSL_CIPHER_get_name(ssl_cipher)
                );
        }

        /**
                如果给定了--method选项,则输出用于握手的方法的版本
       */
        if (method) {
                BIO_printf(
                        bp,
                        "--- Method: %s\n",
                        SSL_get_version(ssl)
                );
        }

        /**
       * 如果给定了--chain,则打印完整的chain。
       */
        if (chain) {
                /**
               * 获取对等证书链。
               */
                if (is_null(fullchain = SSL_get_peer_cert_chain(ssl))) {
                        BIO_printf(bp, "Error: Could not get certificate chain from %s.\n", url);
                        goto on_error;
                }

                BIO_printf(bp, "--- Certificate Chain:\n");

                /**
               * 输出证书链
               */
....

                        /**
                       * 证书的输出签名算法
                       */
                        if (sig_algo) {
                                if (pad_tfmt) {
                                        BIO_printf(bp, "%s%7s", "\n", "");
                                } else {
                                        pad_tfmt = 1;
                                }

#if OPENSSL_VERSION_NUMBER >= 0x10100000L
                                X509_get0_signature(&asn1_sig, &sig_type, tcrt);
#else
                                sig_type = tcrt->sig_alg;
                                asn1_sig = tcrt->signature;
#endif

                                BIO_printf(bp, "--- Signature Algorithm: ");
                                sig_type_err = i2a_ASN1_OBJECT(bp, sig_type->algorithm);

                                if (is_error(sig_type_err, -1) || is_error(sig_type_err, 0)) {
                                        BIO_printf(bp, "Could not get signature algorithm.");
                                }
                        }


...

                crtname = X509_get_subject_name(crt);
                pubkey = X509_get_pubkey(crt);

                /**
               * 输出证书主题信息
               */
                if (subject) {
                        BIO_printf(bp, "--- Subject: ");
                        X509_NAME_print_ex(bp, crtname, 0, XN_FLAG_SEP_COMMA_PLUS);
                        BIO_printf(bp, "\n");
                }

                /**
               * 输出证书颁发者信息。
               */
                if (issuer) {
                        BIO_printf(bp, "--- Issuer: ");
                        X509_NAME_print_ex(bp, X509_get_issuer_name(crt), 0, XN_FLAG_SEP_CPLUS_SPC);
                        BIO_printf(bp, "\n");
                }

                if (bits) {
                        BIO_printf(bp, "%s%d\n", "--- Bits: ", EVP_PKEY_bits(pubkey));
                }

...

                /**
               * If --signature-algorithm option was given,
               * 证书的输出签名算法
               */
                if (sig_algo) {
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
                        X509_get0_signature(&asn1_sig, &sig_type, crt);
#else
                        sig_type = crt->sig_alg;
                        asn1_sig = crt->signature;
#endif

                        BIO_printf(bp, "--- Signature Algorithm: ");
                        sig_type_err = i2a_ASN1_OBJECT(bp, sig_type->algorithm);
                        BIO_printf(bp, "\n");

                        if (is_error(sig_type_err, -1) || is_error(sig_type_err, 0)) {
                                BIO_printf(bp, "Error: Could not get signature algorithm.\n");
                        }
                }

                /**
               * 输出不在前/不在后时间戳的范围
               */
                if (validity) {
                        BIO_printf(bp, "%s", "--- Validity:\n");
                        BIO_printf(bp, "%4s%s", "", "--- Not Before: ");
                        ASN1_TIME_print(bp, X509_get_notBefore(crt));
                        BIO_printf(bp, "\n");
                        BIO_printf(bp, "%4s%s", "", "--- Not After: ");
                        ASN1_TIME_print(bp, X509_get_notAfter(crt));
                        BIO_printf(bp, "\n");
                }

                /**
               * 输出原始证书内容
               */
                if (raw) {
                        BIO_printf(bp, "\n");
                        PEM_write_bio_PUBKEY(bp, pubkey);
                        BIO_printf(bp, "\n");
                        PEM_write_bio_X509(bp, crt);
                        BIO_printf(bp, "\n");
                }
        }

....

        return EXIT_FAILURE;
}

If you need the complete source code, please add the WeChat number (c17865354792)
https://i-blog.csdnimg.cn/direct/9f63266da8394498a409c91379f4b78b.png
https://i-blog.csdnimg.cn/direct/85350a63c84d42d4b801ad92a5974b8a.png
https://i-blog.csdnimg.cn/direct/73f2087e71e14b01bcfe4819be6ebb77.pnghttps://i-blog.csdnimg.cn/direct/ed09e83465fe4b2ca866b994c8d82e89.png
https://i-blog.csdnimg.cn/direct/9cd15e4b99594482a6b47fdce40d2fee.png
https://i-blog.csdnimg.cn/direct/d46fee7cdd0e41b29324859214e12bd0.png用于创建一个安全的SSL/TLS连接。它接受一系列下令行参数,如–bits, --chain, --cipher, --issuer, --method, --no-sni, --quiet, --raw, --serial, --signature-algorithm, --subject, --validity, --help, 和 --version。这些参数允许用户指定他们想要从服务器获取的信息类型。一旦创建了与SSL/TLS服务器的连接,将根据用户指定的参数获取证书信息。
总结
SSL/TLS服务器的证书是网络安全不可或缺的一部分,不仅提供了身份验证和数据加密,还加强了用户对网站安全性的信心。随着网络攻击的日益复杂和频仍,理解和正确实行SSL/TLS证书变得尤为告急。
We also undertake the development of program requirements here. If necessary, please follow the WeChat official account 【程序猿编码】and contact me
参考:1.https://www.idc.net/help/229710/
2.https://learn.microsoft.com/zh-cn/azure/iot-hub/reference-x509-certificates

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 深入分析SSL/TLS服务器的证书(C/C++代码实现)