# SSL-TLS **Repository Path**: fmldd/ssl-tls ## Basic Information - **Project Name**: SSL-TLS - **Description**: 介绍SSL/TLS相关原理和应用,包含一些经典加密,签名,摘要算法的具体实现,证书,秘钥,数字签名等的含义,X509和PKCS标准与SSL/TLS相关的内容,以OpenSSL为工具理论实践相结合,第八节给出多版本的SSL双向认证的代码具体实现; - **Primary Language**: Go - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 12 - **Forks**: 8 - **Created**: 2020-12-30 - **Last Updated**: 2024-10-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 1 SSL/TLS概述及历史 本文旨在描述SSL/TLS的原理及应用,完全了解相关原理,涉及的知识面较广3,4,5节主要用来介绍加密,摘要,签名等基础知识,同时也会挑选一些经典的算法,描述其实现过程;6节介绍密钥证书及相关标准,这些标准规定了一些算法实现,密钥/证书的格式等信息;7节介绍SSL认证过程;8节介绍SSL双认证在编码中的应用; SSL(Secure Sockets Layer 安全套接字协议),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供**安全**及**数据完整性**的一种**安全协议**,SSL是TLS的前身; | 协议 | 年份 | | --- | --- | | SSL 2.0 | 1995 | | SSL 3.0 | 1996 | | TLS 1.0 | 1999 | | TLS 1.1 | 2006 | | TLS 1.2 | 2008 | | TLS 1.3 | 2018 | 在7层网络结构中SSL/TLS工作在传输层之上,属于**会话层**协议,如下图所示: ![\[图1\]7层网络结构](./pic/7层网络结构.png) # 2 OpenSSL OpenSSL是一个功能齐全且强大用于传输层安全性(TLS)和安全套接字层(SSL)协议的工具包;它也是一个通用加密库;包含各种对称加密和非对称加密等算法的实现,在后续章节需要使用OpenSSL相关工具,Win系统可以[下载](https://slproweb.com/products/Win32OpenSSL.html)并安装该工具,相关命令可通过帮助文档查看: ``` //帮助 openssl help //子命令帮助 openssl [子命令] -help openssl rsa -help ``` # 3 加密算法 数据加密的基本过程就是将原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段"密文",使其只能在输入相应的密钥之后才能显示出原文,通过这样的途径来达到保护数据不被非法人窃取、阅读的目的;该过程的逆过程为解密,即将该编码信息转化为其原来数据的过程; ## 3.1 对称加密 采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密;对称加密算法的优点是速度快、效率高;缺点是如果任意一方的密钥被泄露,那么加密信息也就不安全,常见的对称加密算法如下: | 名称 | 说明 | | --- | --- | | DES | Data Encryption Standard,数据加密标准算法 | | 3DES | Triple DES、DESede,进行了三重DES加密的算法 | | AES | Advanced Encryption Standard,高级数据加密标准 | DES/AES算法都是以**位运算**为基础的,往往效率比较高,对称加密算法比较常见和常用,这里便不做展开; ## 3.2 非对称加密 非对称加密算法需要两个密钥(公钥和私钥),公钥与私钥是一对,如用公钥对数据进行加密,只有用对应的私钥才能解密,下面简单介绍下目前被广泛应用的RSA非对称加密的实现; ### 3.2.1 RSA加密解密 | 步骤 | 说明 | 描述 | 备注 | | --- | --- | --- | --- | | 1 | 找出质数 | P ,Q | | 2 | 最小公倍数 | N = P * Q | | 3 | 欧拉函数(N内和N互质的整数个数) | φ(N) = (P-1)(Q-1) | | 4 | 计算公钥Pb| 1 < Pb < φ(N) | E的取值必须是整数E 和 φ(N) 必须是互质数,实际应用多用(65537) | | 5 | 计算私钥Pv | Pb * Pv % φ(N) = 1 | | 6 | 加密 | D = (S ^ Pb) % N | D:密文 S:明文 | | 7 | 解密 | S =(D ^ Pv) % N | D:密文 S:明文 | **公钥包含数据Pb和N** **私钥包含数据Pv和N** 示例: ``` 1. P = 3; Q = 11; S = 2; 2. N = P * Q = 3 * 11 = 33; 3. φ(N) = (P - 1)(Q - 1) = (3 - 1) * (11 - 1) = 20; 4. PublicKey: Pb (1 φ(N)) -> (1 20) -> {3, 7, 9, 11, 13, 17, 19} -> Pb = 3; 5. PrivateKey: Pv Pb * Pv % φ(N) = 1 -> 3 * Pv % 20 = 1 -> Pv = {7, 27 ...} -> Pv = 7; 6. 加密: D = (S ^ Pb) % N = (2 ^ 3) % 33 = 8; 7. 解密: S = (D ^ Pv) % N = (8 ^ 7) % 33 = 2; ``` 通过上面的算法过程可看出,RSA算法是以**幂模运算**为基础的,已知公钥(Pv和N),要破解该算法加密的数据,可以对N(N = P * Q)进行质因数分解,试出P,Q的取值,所以RSA加密的安全性取决于选取P,Q的大小,P,Q在选取较大的质数时,该算法对于性能的消耗也较高; *RSA获取公私钥的过程,在数学中被称为陷门单向函数,通过P,Q乘积容易计算N,反之N通过质因数分解求P, Q却十分困难,随着P, Q值的增加,质因数分解难度呈几何式增长;由于使用了模运算,公钥和私钥是无法相互推导的,比如第五步的Pv可以取7,27...* # 4 摘要算法 摘要算法的主要特征是过程中不需要密钥,并且经过摘要的数据大都无法被还原(摘要过程大都是有损的),只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的摘要,摘要算法主要用与抽取数据的特征,常见摘要算法如下: | 名称 | 说明 | | --- | --- | | MD | Message Digest algorithm 消息摘要算法版本 | | SHA | Secure Hash Algorithm 安全散列算法 | | MAC | Hash Message Authentication Code 散列消息鉴别码 | ## 4.1 MD5 **1.数据填充** 对源数据进行填充,使其变成一个(N\*512-64)位大小的数据 填充方法:在消息后面进行填充,填充第一位为1,其余为0; **2、添加消息长度** 再填充上原数据的长度,可用来进行的存储长度为64位。如果数据长度大于2^64,则只使用其低64位的值; **3、数据处理** **3.1** 将数据按每个512位为一组进行分组,每组里面分成16个32位,也就是16个四字节的数据块; **常量** ``` A=0x01234567 B=0x89abcdef C=0xfedcba98 D=0x76543210 ``` **线性函数** ``` F(X,Y,Z) = (X & Y) | ((~X) & Z); G(X,Y,Z) = (X & Z) | (Y & (~Z)); H(X,Y,Z) = X ^ Y ^ Z; I(X,Y,Z) = Y ^ (X | (~Z)); ``` **数据处理函数** ``` FF(a,b,c,d,Mj,s,Ti)表示a=b+((a+F(b,c,d)+Mj+Ti)<< (1 20) -> {3, 7, 9, 11, 13, 17, 19} -> Pb = 3; 5. PrivateKey: Pv Pb * Pv % φ(N) = 1 -> 3 * Pv % 20 = 1 -> Pv = {7, 27 ...} -> Pv = 7; 6. 签名: D = (S ^ Pv) % N = (2 ^ 7) % 33 = 29; 7. 验签: S = (D ^ Pb) % N = (29 ^ 3) % 33 = 2; ``` 通过学习RSA的加密/解密过程和签名/验签的过程也许你已经发下两者之间的差异: **1. 公钥加密,私钥解密** **2. 私钥签名,公钥验签** 参考上面的示例你完全可以自行尝试私钥加密,公钥解密也是完全可行,但是这种做法是**错误**的;对于最基本的 RSA 方案,这么做是可以顺利地完成加解密的;但是仅仅是理论上成立;因为在实际中为了避免攻击,密码方案中每个参数的选取都有非常严格的要求;公钥(Pb) 和私钥(Pv) 的要求就是截然不同的;如果按照非对称的密码标准生成了密钥对,把公钥留下来解密,把私钥公布出去;我的公钥其实就是私钥,但这个公钥显然是不满足密码标准的要求的,原因如下: 1.私钥不能太小,因为如果私钥 < N ^(1/4),就可以在 log N 的线性时间内找到私钥; 2.公钥也不能太小,为了可以抵挡广播攻击;对于1024bit 的N,私钥至少是 256bit 的; 而公钥取较小值也是被允许的,以公钥常取的65537为例只有17bit, 如果取一个 17bit 的私钥,密码方案的安全性就会很差,且 RSA 的加解密多为大指幂模运算,效率很低;公钥取 65537(0x010001),二进制表示中只有2位为1的质数,解密时利用中国剩余定理可以提高效率; 综上所述,仅从基础的原理角度,RSA 的公私钥互换也可以成功加解密;但公钥和私钥有着完全不同的要求,这些要求保证了 RSA 加密方案的安全高效;在这种情况下,公钥用来公开并加密,私钥用来保留解密,**不可互换**; 公钥加密,私钥解密:接受别人发送加密数据,只有自己可以解密,你对外公布的即是公钥,自己保有的即是私钥;私钥签名,公钥验签:自己给别人发送防伪的数据,保留在手中用作添加防伪标识(签名)的即是私钥,别人检查防伪标识(验签)的即是公钥;但公私钥的生成和选取又不是随意的,必须满足想关的密码学标准; # 6 密钥与证书 ## 6.1 数字证书与X509 数字证书是指在互联网通讯中标志通讯各方身份信息的一个数字认证,人们可以在网上用它来识别对方的身份;X509标准规定了数字证书所包含的身份信息,数字认证等格式,以及识别身份的过程;该标准是由国际电信联盟(ITU-T)制定的数字证书标准,提供了通信实体的鉴别机制(数字证书验证方法),并规定了实体鉴别过程中广泛适用的数字证书的结构(证书的格式),下面通过OpenSSL生成一个证书,并查看其内容: **证书创建** 下面通过RSA密钥对来生成一个自签名私有证书(跟证书),流程如下: | 步骤 | 说明 | | --- | --- | | 1.创建RSA密钥对 | 生成公钥和私钥,公钥是构成证书的内容主要部分,私钥用作签名 | | 2.生成证书基础内容 | 公钥,发行人信息,有效期等构成证书的基本内容 | | 3.生成数字签名 | 使用RSA私钥对证书基础内容进行签名 | | 4.合成完整证书 | 将生成数字签名添加到证书基础内容后面生成完整的证书 | 使用OpenSSL一个命令就可以搞定上面的所有动作,创建命令如下(root.key->RSA私钥匙文件;root.crt ->证书文件 ): ``` openssl req -newkey rsa:2048 -nodes -keyout root.key -x509 -days 365 -out root.crt ``` 填写证书发行人等相关信息: ``` Generating a RSA private key ...............................................................................................+++++ ..............................+++++ writing new private key to 'root.key' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:CN State or Province Name (full name) [Some-State]:root Locality Name (eg, city) []:root Organization Name (eg, company) [Internet Widgits Pty Ltd]:root Organizational Unit Name (eg, section) []:root Common Name (e.g. server FQDN or YOUR name) []:root Email Address []: ``` **X509证书属性** | 描述 | 说明 | | --- | --- | | CN(Country Name) | ISO国家代码(两位字符) | | ST(State or Province Name) | 所在省份 | | L(Locality Name) | 所在城市 | | O(Organizational Name) | 公司名称 | | OU(Organizational Unit Name) | 部门名称 | | Common Name | 申请SSL证书的域名 | | Email Address | 邮箱 | | A Chanllenge Password | 密码 | | Basic Constraints | CA证书中间证书为TRUE;终端证书为FALSE | 注:签发终端证书时Organizational Name/Organizational Unit Name需要区别,部分平台会验证域名(Common Name即 上面的CN=),签发服务器证书时需要注意CN字段值; **证书内容** 查看命令:openssl x509 -noout -text -in root.crt ``` Certificate: Data: //版本 Version: 3 (0x2) //序列号 Serial Number: 1d:f1:42:0a:02:8e:93:1e:07:ef:77:d9:f4:13:bd:ef:92:f9:a2:82 //签名算法 Signature Algorithm: sha256WithRSAEncryption //发行人 Issuer: C = CN, ST = root, L = root, O = root, OU = root, CN = root //有效期 Validity Not Before: Jan 21 10:00:48 2021 GMT Not After : Jan 21 10:00:48 2022 GMT //主题 Subject: C = CN, ST = root, L = root, O = root, OU = root, CN = root //公钥信息 Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:e3:12:5c:f4:51:b9:6e:6e:70:6f:36:e1:ab:8e: 48:41:8f:b2:a3:2e:14:a2:62:66:b0:0a:c0:cf:32: 53:7b:17:cc:e8:e2:c1:20:45:a0:eb:30:0f:2b:d5: da:29:1c:60:73:b3:33:bd:28:f5:df:2b:26:a5:ec: d4:07:18:e9:55:d3:41:39:33:cb:35:6b:3f:27:03: e5:ba:5c:3b:67:85:d2:e4:79:49:27:d0:7c:c9:c6: ee:2a:a1:7a:3e:f4:71:de:82:7f:b7:63:92:55:bb: 1a:83:49:87:fc:34:1c:39:12:35:5b:fb:c1:bc:b8: 52:15:aa:a3:b5:d0:cb:49:3a:90:68:58:d1:84:49: 46:ee:52:73:95:12:54:6e:a2:96:2a:75:d9:f4:b1: 03:4a:03:c9:72:a7:51:e0:79:bc:19:31:6b:e6:33: 0b:5f:92:bb:fa:99:dc:d5:dc:b2:29:c9:0b:19:3e: da:dd:27:66:66:53:a9:2f:68:c3:32:23:d5:9b:36: 80:b5:a7:e4:23:f6:6c:f7:fd:f7:b1:c0:37:78:aa: e8:2f:ca:96:a4:11:a2:10:76:38:f6:1e:66:37:76: e7:f0:db:23:5e:8f:23:b1:63:e0:13:26:0b:5c:c9: fb:05:88:d6:9f:9b:3a:09:06:3c:57:25:22:63:65: b4:d1 Exponent: 65537 (0x10001) //v3版本字段 X509v3 extensions: X509v3 Subject Key Identifier: 6E:3F:72:72:A3:42:69:DA:73:FD:87:D0:BD:1F:87:48:50:EE:4E:67 X509v3 Authority Key Identifier: keyid:6E:3F:72:72:A3:42:69:DA:73:FD:87:D0:BD:1F:87:48:50:EE:4E:67 //终端证书FALSE/非终端证书为TRUE X509v3 Basic Constraints: critical CA:TRUE //数字签名 Signature Algorithm: sha256WithRSAEncryption 8b:5e:6f:be:69:7f:86:bc:f0:96:09:b9:24:1d:b7:07:c7:c3: 42:d3:d1:3f:f7:ac:c1:c4:1d:bd:d0:07:a4:20:3c:78:90:9a: e1:7f:02:53:68:97:9c:ba:01:92:c2:1d:1e:5d:6d:d0:41:c0: 2d:dd:29:22:4c:e0:9b:5f:eb:76:a0:8c:09:82:da:64:54:76: c1:e9:73:52:25:ed:e8:f5:ec:4c:8e:65:ee:7b:d2:c6:bf:ba: f3:42:6a:28:a2:35:92:86:4e:b1:f4:75:af:82:34:6f:08:94: 61:70:f3:ff:9d:ca:8f:ea:5f:e8:16:46:a1:05:8f:38:67:b0: d9:d2:d0:d6:2d:94:a9:37:a6:29:12:7e:13:f3:71:71:d7:24: 0c:56:d2:58:86:4b:5a:28:85:b8:59:e7:3e:10:8b:60:05:d2: 96:a8:75:fe:88:32:a2:4d:fd:e1:23:7a:e4:ff:06:12:b9:89: f7:03:f2:96:3f:67:f0:7d:0c:aa:3d:02:6d:18:4d:6f:e0:bb: 2e:85:90:d9:4c:cf:c2:65:78:b5:f0:ee:95:e3:7d:d0:f5:fd: db:05:89:c4:8e:07:51:16:8e:a7:85:67:92:7e:69:b6:32:74: 8f:13:15:29:de:0f:db:9d:37:9d:62:3b:90:c9:e6:8d:e5:78: 92:6d:b9:65 ``` 通过查看证书内容可以看到,证书中包含了:版本,序列号,签名算法名称,有效期,公钥信息,数字签名等内容,数字签名和平时使用的公章/防伪标记十分类似; X509包含很多版本(本文证书使用X.509V3),细则请参考[RFC5280-X.509V3](https://tools.ietf.org/html/rfc5280#page-9),上面提到了Common Name用作域名保护,但是每个整数只能配置一个域名,对于域名站点较多的公司,证书管理尽可能会十分混乱,X.509V3为例解决这个痛点,添加了SAN(Subject Alternative Name)扩展,利用单个证书保护多个域名,且支持一个域名下所有无限个子域名; ## 6.2 PKCS 通过上一节了解到X509标准定义证书所包含的内容,不知你是否有疑问,X509主要是针对数字证书的,那公私钥是如何存储的呢,RSA算法如何定义,公私钥如何取值的呢,等等?PKCS就是来定义这些东西,PKCS一些标准是对X509的补充和拓展,还有更多是对密码学中一些算法和数据格式的规定;PKCS是由美国RSA数据安全公司及其合作伙伴制定的一组公钥密码学标准,其中包括证书申请、证书更新、证书作废表发布、扩展证书内容以及数字签名、数字信封的格式等方面的一系列相关协议; | 名称 | 说明 | | --- | --- | | PKCS#1 | 定义RSA公开的密钥算法加密和签名机制,主要用于组织PKCS#7中所描述的数字签名和数字信封 | | PKCS#3 | 定义Diffie-Hellman密钥交换协议 | | PKCS#5 | 描述一种利用从口令派生出来的安全密钥加密字符串的方法。使用MD2或MD5 从口令中派生密钥,并采用DES-CBC模式加密。主要用于加密从一个计算机传送到另一个计算机的私人密钥,不能用于加密消息 | | PKCS#6 | 描述了公钥证书的标准语法,主要描述X.509证书的扩展格式 | | PKCS#7 | 定义一种通用的消息语法,包括数字签名和加密等用于增强的加密机制,PKCS#7与PEM兼容,所以不需其他密码操作,就可以将加密的消息转换成PEM消息 | | PKCS#8 | 描述私有密钥信息格式,该信息包括公开密钥算法的私有密钥以及可选的属性集等 | | PKCS#9 | 定义一些用于PKCS#6证书扩展、PKCS#7数字签名和PKCS#8私钥加密信息的属性类型 | | PKCS#10 | 描述证书请求语法 | | PKCS#11 | 称为Cyptoki,定义了一套独立于技术的程序设计接口,用于智能卡和PCMCIA卡之类的加密设备 | | PKCS#12 | 描述个人信息交换语法标准;描述了将用户公钥、私钥、证书和其他相关信息打包的语法 | | PKCS#13 | 椭圆曲线密码体制标准 | | PKCS#14 | 伪随机数生成标准 | | PKCS#15 | 密码令牌信息格式标准 | *PKCS#1:详细的RSA算法过程,填充,密钥(公/私)存储格式; PKCS#8:私钥信息语法和加密私钥语法,其中私钥加密使用了PKCS#5标准,不仅仅支持RSA,灵活性优于PKCS#1* 根据自己需要选择相关标准,创建/保存/解析秘钥(创建,解析标准应当一致); **常见扩展名** **1 DER 扩展名** .DER = DER扩展用于二进制DER编码证书; 这些文件也可能承载CER或CRT扩展; 正确的说法是“我有一个DER编码的证书”不是“我有一个DER证书”; **2 PEM 扩展名** .PEM = PEM扩展用于不同类型的X.509v3文件,是以“ - BEGIN ...”前缀的ASCII(Base64)数据; **3 常见的扩展** **3.1 CRT 扩展名** .CRT = CRT扩展用于证书。 证书可以被编码为二进制DER或ASCII PEM;CER和CRT扩展几乎是同义词; 最常见的于Unix 或类Unix系统; **3.2 CER扩展名** CER = .crt的替代形式(Microsoft Convention)您可以在微软系统环境下将.crt转换为.cer(.both DER编码的.cer,或base64 [PEM]编码的.cer;.cer文件扩展名也被IE识别为 一个运行MS cryptoAPI命令的命令(特别是rundll32.exe cryptext.dll,CryptExtOpenCER),该命令显示用于导入和/或查看证书内容的对话框; **3.3 KEY 扩展名** .KEY = KEY扩展名用于公钥和私钥PKCS#8。 键可以被编码为二进制DER或ASCII PEM; **3.4 CSR 扩展名** .CSR是Certificate Signing Request的英文缩写,即证书请求文件,证书申请者只要把CSR文件提交给证书颁发机构后,证书颁发机构使用其根证书私钥签名就生成了证书公钥文件,也就是颁发给用户的证书; **PKCS#1密钥** 使用OpenSSL创建PKCS#1格式RSA的密钥对,并查看其内容: ``` //生成私钥 openssl genrsa -out private_pkcs1.key 2048 //生成公钥 openssl rsa -in private_pkcs1.key -pubout -out public_pkcs1.key ``` 通过OpenSSL命令可以查看私钥中包含的数据;查看私钥内容命令如下: ``` openssl rsa -noout -text -in private_pkcs1.key ``` 了解私钥包含的数据之前,先回顾下*3.2.1节RSA加密解密的内容*,对照理解十分简单,私钥内容如下: **RSA加密解密** | 步骤 | 说明 | 描述 | 备注 | | --- | --- | --- | --- | | 1 | 找出质数 | P ,Q | | 2 | 最小公倍数 | N = P * Q | | 3 | 欧拉函数(N内和N互质的整数个数) | φ(N) = (P-1)(Q-1) | | 4 | 计算公钥Pb| 1 < Pb < φ(N) | E的取值必须是整数E 和 φ(N) 必须是互质数,实际应用多用(65537) | | 5 | 计算私钥Pv | Pb * Pv % φ(N) = 1 | | 6 | 加密 | D = (S ^ Pb) % N | D:密文 S:明文 | | 7 | 解密 | S =(D ^ Pv) % N | D:密文 S:明文 | ``` RSA Private-Key: (2048 bit, 2 primes) //modulus:N(P * Q)的值 modulus: 00:cf:a0:58:a0:be:39:26:9a:01:23:23:d9:f8:39: cf:df:7f:58:6b:c1:84:b1:1b:c9:63:e8:9c:2d:55: 36:a9:54:0d:68:4b:fa:ec:11:5a:bb:03:0d:55:a5: 7c:67:e0:f3:5d:43:36:fe:6b:59:74:d2:3a:fc:92: b3:8d:1e:eb:75:37:41:25:f7:eb:a4:5c:ba:6d:4b: 7e:eb:cf:75:49:ee:9e:c5:60:c2:55:91:7b:6a:e3: 6b:db:b5:46:dc:20:5a:5b:78:a9:c2:a7:45:0b:04: 60:d3:cc:ad:16:78:21:5b:07:05:b9:9b:91:ba:76: 4b:21:d0:9a:46:04:17:c0:61:17:0b:65:7b:fe:83: 32:4b:b4:3d:53:6c:5c:91:c4:03:96:34:43:59:85: 9d:48:9e:b3:ea:11:2a:35:57:23:22:70:40:7a:90: b7:06:13:67:38:fa:ae:57:c1:ce:7f:1b:0a:d4:36: d9:66:8d:07:5b:50:f2:20:c7:88:27:7b:ee:4d:13: 8d:eb:23:0f:d2:65:91:42:d7:13:8f:6b:b7:2c:c0: 61:32:7d:10:55:89:05:30:a3:2c:b7:8c:1e:ef:3f: c4:17:76:46:e5:63:02:a1:ea:d0:a8:df:c8:46:10: 1c:99:8d:4e:9e:1c:50:23:5e:e1:34:a5:c8:35:bc: af:37 //publicExponent:公钥Pb publicExponent: 65537 (0x10001) //publicExponent:私钥Pv privateExponent: 00:b8:47:11:c2:81:73:0a:0e:d0:09:97:4d:63:a2: 34:1c:a2:d3:20:b8:9e:83:ef:2c:9c:af:1c:c6:a3: 86:23:5d:c5:94:97:33:3b:08:e0:25:71:bd:09:12: 1a:cb:bb:fd:63:fe:3f:36:2a:a3:38:2b:4b:4d:57: 0b:63:aa:58:c3:2d:6a:76:3f:87:f6:d6:92:64:65: 08:5e:55:40:3d:81:3b:8b:e5:a8:35:f8:2c:b9:1e: e6:61:17:90:af:d9:4b:39:3a:6c:39:a6:ea:81:af: be:1c:dd:e1:96:3a:c3:28:1e:a0:77:2f:5b:75:4a: 7e:be:a1:7d:fb:bc:b0:33:fe:9f:0a:ea:55:9b:32: e7:fa:4c:04:e9:97:1b:d9:a8:c3:e4:65:69:3f:4b: 2a:cb:3b:1a:10:34:db:17:87:ff:fd:b5:36:a6:1c: 80:c2:ba:1c:72:09:48:e2:d7:e1:01:08:79:a8:04: 84:1a:03:7c:b7:ce:2a:bf:7d:f9:6b:17:a7:75:f1: fe:5d:69:03:02:9b:e0:49:5f:ee:39:3a:1f:fd:84: 5a:7f:e0:b9:4d:01:21:4c:9b:f6:aa:24:3e:88:a0: 8a:63:64:94:b0:78:e5:49:ae:c4:1f:0f:45:7e:ae: e7:75:8a:7c:47:85:3c:f3:f9:e9:c3:38:2e:97:83: 83:11 //prime1:质数P的值 prime1: 00:e7:dd:3a:c0:ab:9f:55:50:d0:04:25:b5:37:7c: b9:a1:27:72:15:75:39:19:25:cb:39:a0:69:cd:80: ac:ec:0f:8a:b5:ab:12:d5:34:da:a2:42:30:a1:1d: b6:bf:b0:ce:aa:23:f9:f3:f0:2d:97:47:c9:51:d8: 06:7e:c6:8b:88:7b:79:2b:14:68:3f:44:41:ce:19: f5:07:95:86:da:61:72:a1:20:6e:ac:5a:fe:b1:1c: 20:1b:cf:64:5e:bf:38:a0:31:ff:df:43:df:ae:55: 67:f8:f2:75:47:3e:d7:d9:38:8e:cc:23:19:db:b9: 0c:4c:96:79:39:c1:4e:8b:49 //prime2:质数Q的值 prime2: 00:e5:3d:38:b3:f7:98:6b:1d:04:8e:b6:30:8e:7f: b5:33:e9:7e:e2:1f:96:c6:a0:5d:36:c0:4b:8d:ae: 00:4e:bc:4b:7e:1c:a2:47:a9:a0:1c:81:8d:ab:2b: 50:4d:24:ca:9d:cc:db:8c:23:aa:12:bc:6c:5e:16: a3:c3:e2:7a:17:d9:ab:17:64:9b:65:ca:44:b0:05: c3:f7:86:00:8c:89:71:9e:8c:f2:9e:61:fb:7e:3d: 57:0b:79:b4:84:82:4c:37:3b:12:c1:17:9e:70:f5: f8:bc:7f:2e:23:e4:52:50:14:1c:7a:4b:5d:7c:46: 58:10:c3:9c:37:d7:92:e6:7f //exponent1:Pv%(P−1) exponent1: 35:fa:59:c8:2a:ec:1f:f7:7d:45:c9:e5:83:32:65: 20:b0:47:2d:d9:a9:5e:42:5f:2a:5e:3e:e3:59:96: 68:2a:42:17:82:e9:12:7b:88:7e:87:bc:3c:75:e7: 6a:7e:e5:cd:d4:2c:47:6e:dd:9e:3f:57:0d:d7:a5: c3:73:c3:5e:15:a8:a2:af:d0:89:12:01:e3:d2:45: 29:eb:d4:86:18:73:90:31:35:b2:a8:48:6c:c0:e8: 74:b0:f6:63:dc:4e:79:74:ca:65:96:ae:87:2e:58: d3:2a:d6:c4:ac:4b:a1:c5:39:f1:2a:26:c5:f1:31: 7c:ce:f7:0f:cc:01:d6:09 //exponent2:Pv%(Q−1) exponent2: 00:8c:a7:02:aa:91:89:48:77:76:7b:65:d7:e7:26: fe:74:4a:98:da:7f:4d:50:b3:c6:5d:b8:a0:e2:de: 57:3b:f8:16:4d:db:9d:ca:60:8e:67:cf:29:b0:53: 94:22:b1:27:4c:a9:0c:30:31:15:c2:07:30:6a:8e: 73:8e:d5:df:d9:d0:69:44:a2:2b:22:ca:77:58:95: 41:07:17:86:30:b4:88:2d:23:08:1e:1b:ba:66:5b: 96:93:e8:e9:d4:bf:e9:a9:9d:aa:45:25:8b:c6:7d: 75:4f:9a:ad:21:c2:8e:aa:a9:9c:4b:81:00:6c:26: f1:13:e0:21:72:b5:e7:61:5b //coefficient:(Q-1)%P coefficient: 00:d2:0d:9a:f1:1e:84:c0:b6:95:5d:78:e4:02:04: 94:8a:75:a4:e7:02:76:96:d7:86:4c:65:76:d9:0c: f5:71:b9:f3:b8:70:82:28:09:c9:44:41:d1:22:83: 2a:f7:4b:2d:e9:c6:b9:ae:58:a2:ef:50:0b:9c:88: 61:98:f0:72:c3:64:1e:d9:f6:5c:de:a3:04:58:f2: ad:53:a3:a0:f8:24:e9:c7:67:43:ae:4d:dc:4b:64: 19:6b:77:94:fa:d4:0c:3e:d2:b3:91:cc:1d:9d:a3: aa:5c:85:b5:e6:26:2d:51:4b:0f:e5:8b:3b:82:4e: 3d:05:e2:63:1e:fb:4d:d8:d5 ``` 通过上面的数据我们看出生成私钥文件保存的内容,都是RSA加密和签名过程中不可或缺的数据;私钥文件中同样包含了公钥的数据,这也是为什么利用私钥文件使用OpenSSL生成的公钥文件总是一样;65537作为经典的公钥也被应用到实践中,所以私钥文件一但被窃取那安全也不复存在,为了更好的保存私钥文件,PKCS#8应运而生,PKCS#8提供基于密码的私钥存储格式; **PKCS#8私钥** PKCS#8提供更安全,跟通用的私钥存储格式;通过OpenSSL可以将PKCS#1的私钥匙转换为PKCS#8格式,命令如下: ``` //pkcs8格式的私钥默认是要输入密码,但这并不是必须的,可以通过指定 -nocrypt 来跳过密码; openssl pkcs8 -topk8 -inform PEM -in private_pkcs1.key -outform pem -out private_pkcs8_new.key ``` 无论PKCS#1的密钥,还是PKCS#8的私钥,存储私钥里的数据内容(质数P,Q,N,Pb,Pv等)总是一样,不同只是密钥存储时的数据结构;可以通过查看私钥的命令查看转换后PKCS#8私钥的内容和PKCS#1完全一致: ``` openssl rsa -noout -text -in private_pkcs1.key ``` ## 6.3 CA证书与自签名证书 CA证书就是有权威证书颁发机构签发的证书,这里不再多做赘述;自签名证书分为自签名私有证书和私有CA签名证书两种;自签名私有证书无法被吊销,私有CA签名证书可以被吊销,通过OpenSSL创建自签名证书,命令如下: **自签名私有证书** ``` :: x509v3配置生成 echo basicConstraints=CA:TRUE > v3CA.ext :: 生成根证书私钥(pem文件) openssl genrsa -out root.key 4096 :: 生成根证书签发申请文件(csr文件) openssl req -new -key root.key -out root.csr -subj "/CN=rootCA/C=CN/ST=rootprovince/L=rootcity/O=rootorganization/OU=rootgroup" :: 自签发根证书(cer文件) openssl x509 -req -extfile v3CA.ext -days 3650 -signkey root.key -in root.csr -out root.crt ``` 使用自己生成的一个RSA秘钥对,生成证书请求文件,并签发证书的过程被称为自签名过程,自签名一般用于跟证书的创建; **私有CA签名证书** 利用上面已生成的自签名私有证书创建私有CA签名证书: ``` :: 生成中间证书私钥(pem文件) openssl genrsa -out mid.key 4096 :: 生成中间证书签发申请文件(csr文件) openssl req -new -key mid.key -out mid.csr -subj "/CN=midCA/C=CN/ST=midprovince/L=midcity/O=midorganization/OU=midgroup" :: 签发中间证书(cer文件) openssl x509 -req -extfile v3CA.ext -days 3650 -CA root.crt -CAkey root.key -CAserial root.srl -CAcreateserial -in mid.csr -out mid.crt :: 使用CA证书验证中间端证书 openssl verify -CAfileke root.crt mid.crt ``` 自签名私有证书一般用于跟证书的创建,使用自签名私有证书签发的就是私有CA签名证书;自签名证书和CA证书是完全不同的概念;使用证书权威颁发机构的私钥签发的证书叫CA证书;开发者自己创建RSA私钥,再用该密钥签发的证书叫自签名证书,私有CA签名证书是自签名证书的子集,这里**私有CA签名**含义是将自签名私有证书当做可信的跟证书,由此签发而来的证书; ## 6.4 证书链 证书的签发需要私钥,跟证书是由跟私钥自签名签名得到的(参考6.3节),跟证书无法被吊销,跟私钥一但被窃取,基于此构建的安全体系将彻底崩溃,如何能尽可能的保护跟私钥的安全,让其不被读取,证书链的概念应运而生,使用跟证书签发中间证书,再由中间证书签发终端证书;中间证书可以是多级的;多级证书链的机构如下: ![\[图2\]证书链](./pic/证书链.PNG) 使用OpenSSL创建证书链并验证,命令如下: ``` :: x509v3配置生成 echo basicConstraints=CA:TRUE > v3CA.ext echo basicConstraints=CA:FALSE > v3Other.ext :: 生成根证书私钥(pem文件) openssl genrsa -out root.key 4096 :: 生成根证书签发申请文件(csr文件) openssl req -new -key root.key -out root.csr -subj "/CN=rootCA/C=CN/ST=rootprovince/L=rootcity/O=rootorganization/OU=rootgroup" :: 自签发根证书(cer文件) openssl x509 -req -extfile v3CA.ext -days 3650 -signkey root.key -in root.csr -out root.crt :: 生成中间证书私钥(pem文件) openssl genrsa -out mid.key 4096 :: 生成中间证书签发申请文件(csr文件) openssl req -new -key mid.key -out mid.csr -subj "/CN=midCA/C=CN/ST=midprovince/L=midcity/O=midorganization/OU=midgroup" :: 签发中间证书(cer文件) openssl x509 -req -extfile v3CA.ext -days 3650 -CA root.crt -CAkey root.key -CAserial root.srl -CAcreateserial -in mid.csr -out mid.crt :: 使用CA证书验证中间证书 openssl verify -CAfile root.crt mid.crt ::合并证书(Win CMD) type mid.crt > CA.crt type root.crt >> CA.crt :: 生成服务端私钥 openssl genrsa -out server.key 4096 :: 生成证书请求文件 openssl req -new -key server.key -out server.csr -subj "/CN=localhost/C=CN/ST=serverprovince/L=servercity/O=serverorganization/OU=servergroup" :: 使用根证书签发服务端证书 openssl x509 -req -extfile v3Other.ext -days 3650 -CA mid.crt -CAkey mid.key -CAserial mid.srl -CAcreateserial -in server.csr -out server.crt :: 使用CA中间证书验证服务端证书 openssl verify -CAfile CA.crt server.crt :: 生成客户端私钥 openssl genrsa -out client.key 4096 :: 生成证书请求文件 openssl req -new -key client.key -out client.csr -subj "/CN=clientCA/C=CN/ST=clientprovince/L=clientcity/O=clientorganization/OU=clientgroup" :: 使用CA中间证书签发客户端证书 openssl x509 -req -extfile v3Other.ext -days 3650 -CA mid.crt -CAkey mid.key -CAserial mid.srl -CAcreateserial -in client.csr -out client.crt :: 使用CA中间证书验证客户端证书 openssl verify -CAfile CA.crt client.crt ``` 中间证书是可以被吊销的,读者可以自行尝试; ## 6.5 小结 X509或PKCS这些标准看起来比较枯燥,但是对于大多数开发者而言并不陌生,仔细回想开发过程中可能或多或少的都接触过,Java中的X509Certificate类,golang的x509包等,都是对这些标准的实践,掌握这些概念同样也会有助于编码; # 7 SSL认证 SSL认证是客户端和服务单双方身份的识别,包含单向认证/双向认证; ## 7.1 SSL单向/双向认证 ![\[图3\]SSL单向认证](./pic/SSL单向认证.PNG) ![\[图4\]SSL双向认证](./pic/SSL双向认证.PNG) SSL使用了非对称加密商量对称加密的密钥,再使用对称加密进行数据传输,这样做的目的自然是对称加密具有更高的效率;图3和图4对于SSL认证的过程做了详细的描述,但是仍然有一些细节可以深究,以双向认证为例(图4),客户端和服务端相互交换了数字证书,并对数字证书进行了校验,如何校验对方的证书,结合上面对于X509的介绍,很容易理解,SSL双向认证时数字证书交换过程如下: ![\[图5\]SSL双向认证数字证书交换过程](./pic/SSL双向认证数字证书交换过程.PNG) 公钥是公开的,在交换完数字证书以后使用第三方的公钥验证秘钥信息真伪; # 8 编码 这一节将通过代码来实现SSL双向认证的过程,使用TCPS实现跨语言(C++/go)的SSL双向认证过程,密钥和证书的生成则通过openssl来实现; 生成证书命令如下: 1.生成自签名根证书 ``` :: 生成根证书私钥(pem文件) openssl genrsa -out root.key 2048 :: 生成根证书签发申请文件(csr文件) openssl req -new -key root.key -out root.csr -subj "/CN=localhost/C=CN/ST=rootprovince/L=rootcity/O=rootorganization/OU=rootgroup" :: 自签发根证书(cer文件) openssl x509 -req -days 365 -extensions v3_ca -signkey root.key -in root.csr -out root.crt ``` 2.通过CA证书签发服务端证书 ``` :: 生成服务端私钥 openssl genrsa -out server.key 2048 :: 生成证书请求文件 openssl req -new -key server.key -out server.csr -subj "/CN=localhost/C=CN/ST=serverprovince/L=servercity/O=serverorganization/OU=servergroup" :: 使用根证书签发服务端证书 openssl x509 -req -days 365 -extensions v3_req -CA root.crt -CAkey root.key -CAserial root.srl -CAcreateserial -in server.csr -out server.crt :: 使用CA证书验证服务端证书 openssl verify -CAfile root.crt server.crt ``` 3.通过CA证书签发客户端证书 ``` :: 生成客户端私钥 openssl genrsa -out client.key 2048 :: 生成证书请求文件 openssl req -new -key client.key -out client.csr -subj "/CN=localhost/C=CN/ST=clientprovince/L=clientcity/O=clientorganization/OU=clientgroup" :: 使用根证书签发客户端证书 openssl x509 -req -days 365 -extensions v3_req -CA root.crt -CAkey root.key -CAserial root.srl -CAcreateserial -in client.csr -out client.crt :: 使用CA证书验证客户端证书 openssl verify -CAfile root.crt client.crt ``` **示例代码:** C/C++:https://gitee.com/fmldd/sslsocket-c Go:https://gitee.com/fmldd/sslsocket-go 以上示例均使用TCP+SSL进行通讯,且通过生成证书实现了双向认证,C/Go均包含客户端和服务端,可交互访问; *注:golang 1.15以上推荐使用X509的SAN扩展证书,使用X509证书需要添加QDEBUG=X509IgnoreCN=0环境变量*