OpenSSL 学习笔记
传输层安全
- 安全通信
- 避免窃听
- 确认另一端连接者的身份
名词解释
CA (Certificate Authority) 证书授权中心,是数字证书颁发和管理的机构。DN (Distinguished Name) 标识名,指定实体身份的字段。CSR (Certificate Signing Request) 数字证书签名请求,其中包含了公钥和DN。
证书机制
根证书:是 CA 机构颁发 SSL 证书的核心,是信任链的起始点。受信任的根证书是属于证书颁发机构(CA),而 CA 机构是验证和颁发 SSL 证书的组织机构。
证书链:由两个环节组成,信任锚证书环节 和 已签名证书环节。信任锚证书环节可以对中间证书签名;中间证书的所有者可以用自己的私钥对另一个证书签名,两者结合就构成证书链。
用户获取 SSL 证书之前,首先生成 证书签名请求 和 私钥,然后将生成的签名请求发送到证书颁发机构,再使用证书颁发机构的根证书的私钥签署用户的 SSL 证书,并将 SSL 证书发回给用户。
中间证书:证书颁发机构不会直接从根目录颁发 SSL 证书,一旦发生错误,颁发者或需求者需要撤销根证书,则使用根证书签名的每个证书都会被撤销信任。为了避免这种风险发生,CA 机构一般会引用中间根。CA 机构使用中间根的私钥来签署用户申请的 SSL 证书。这种中间根的形式可以重复多次,即使用中间根签署另一个中间证书。
根证书CA 和 中间根CA 的区别:
根证书CA是拥有一个或多个受信任的根证书颁发机构,即CA机构已经扎根在主要浏览器的信任库中,而中间根CA或子CA是颁发中间根的证书颁发机构,他们不一定在浏览器的信任库中有根证书,而是将他们的中间根链接到手信任的第三方根,被称为交叉签名。
链式根 和 单一根 之间的区别:
单一根是由CA拥有的,可直接颁发证书。链式根是SubCA用于颁发证书的内容,是一个中间证书,必须链接到第三方受信任的CA。
链式根需要复杂的安装方法,因为中间根需要加载到托管证书的每个服务器和应用程序。
链式根需要受到链接的 CA 支配,无法控制 root 用户,一旦root CA 停业,就会受到巨大的牵连。
证书格式
x509 证书常见扩展名:DER、PEM、CRT、CER。
- .DER
 用于二进制 DER 编码证书。
- .PEM
 用于不同类型的 x509v3 文件,以“-BEGIN”为前缀的 ASCII(或 Base64)数据。
- .CRT
 用于证书,可被编码为二进制 DER 或 ASCII PEM,常见于类 Unix 系统。
- .CER
 CRT 的替代形式(Microsoft Convention),Windows 环境下 .crt 可转为 .cer。
 证书中只包含公钥,没有私钥。
- .KEY
 用于公钥和私钥 PKCS#8,可被编码为二进制 DER 或 ASCII PEM。
- .PFX
 带私钥的证书(包含公钥和私钥)。
 由 Public Key Cryptography Standards #12,PKCS#12标准定义,包含了公钥和私钥的二进制格式的证书形式,以 pfx 或 p12 作为证书文件后缀名。
颁发实战
- 配置文件 - 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
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60- [ca] 
 default_ca=CA_default
 [CA_default]
 policy=policy_match
 new_certs_dir=newcerts
 database=index.txt
 default_md=default
 serial=serial.txt
 default_days=3650
 [policy_match]
 countryName=match
 stateOrProvinceName=match
 organizationName=match
 organizationalUnitName=optional
 commonName=supplied
 emailAddress=optional
 [policy_anything]
 countryName=optional
 stateOrProvinceName=optional
 localityName=optional
 organizationName=optional
 organizationalUnitName=optional
 commonName=supplied
 emailAddress=optional
 [test]
 countryName=CN
 stateOrProvinceName=GuangDong
 organizationName=BGI
 organizationalUnitName=MGIUS
 commonName=MGIUSDICOM
 emailAddress=jiangchuanbiao@genomics.cn
 [req]
 default_bits=1024
 default_keyfile=private_key.pem
 distinguished_name=req_distinguished_name
 attributtes=req_attributes
 [req_distinguished_name]
 countryName=Country Name (2 letter code)
 countryName_min=2
 countryName_max=2
 stateOrProvinceName=State or Province Name (full name)
 localityName=Locality Name (eg, city)
 organizationName=Organization Name (eg, company)
 organizationalUnitName=Organizational Unit Name (eg, section)
 commonName=Common Name (eg. YOUR name)
 commonName_max=64
 emailAddress=Email Address
 emailAddress_max=40
 [req_attributes]
 challengePassword=A challenge password
 challengePassword_min=4
 challengePassword_max=20
 unstructuredName=An optional company name
- 自签名 - 1 
 2
 3
 4
 5
 6
 7
 8- # 1.生成私钥 
 $ openssl genrsa -out server.key 2048
 # 2.生成 CSR (Certificate Signing Request)
 $ openssl req -subj "/C=CN/ST=GD/L=Shenzhen/O=Self/OU=ShuanglongTest/CN=Shuanglong/emailAddress=xjshuanglong@126.com" -new -key server.key -out server.csr
 # 3.生成自签名证书
 $ openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt
- 私有 CA 签名 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14- # 1.创建 CA 私钥 
 $ openssl genrsa -out ca.key 2048
 # 2.生成 CA 的自签名证书
 $ openssl req -subj "/C=CN/ST=Tianjin/L=Tianjin/O=Mocha/OU=Mocha Software/CN=Server CA/emailAddress=test@mochasoft.com.cn" -new -x509 -days 3650 -key ca.key -out ca.crt
 # 3.生成需要颁发证书的私钥
 $ openssl genrsa -out server.key 2048
 # 4.生成要颁发证书的证书签名请求,证书签名请求当中的 Common Name 必须区别于 CA 的证书里面的 Common Name
 $ openssl req -subj "/C=CN/ST=Tianjin/L=Tianjin/O=Mocha/OU=Mocha Software/CN=test2.sslpoc.com/emailAddress=test@mochasoft.com.cn" -new -key server.key -out server.csr
 # 5.用 2 创建的 CA 证书给 4 生成的 签名请求 进行签名
 $ openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt
编程模式
- 建立 SSL 上下文环境后使用 BIO 进行通信
- 基于已建立的 Socket 连接创建 SSL 上下文环境
接口调用步骤: 参考 https://www.cnblogs.com/LittleHann/p/3741907.html
经验总结
- 类封装思想 
 参考自 DCMTK,环境上下文与连接分开封装:
 DcmTLSTransportLayer 用于 SSL 上下文环境初始化、加载秘钥、加载证书、设置验证类型、设置加密套件;
 DcmTLSConnection 用于 SSL 连接握手、重协商、请求认证、读写数据、关闭连接。- 思考结果:为确保 SSL 上下文环境只初始化一次,要么使全局/静态用标志位,要么干脆将上下文换件封装成静态方法,可与 SSL 通信环境同处一个类,也可分开各自为政。 
- 命令行卡死 (Windows + GitBash) 
 执行命令- openssl genrsa -des3 -out test.key 2048后,进程假死,无法执行后续操作,只有结束当前进程。
 在 windows 命令行中执行,会提示输入密码。
 此时可通过传入参数传递密码,而不是通过标准输入传入密码。- openssl genrsa -des3 -passout pass:mima -out test.key 2048
 验证密码正确性:- openssl rsa -in test.key -check,根据提示输入密码短语。
 注:使用 CMD,GitBash 似乎无效即使加入- -passout pass:mima。
- GitBash 执行 DCMTK 测试程序时出现卡死 
 通过任务管理器杀死 DCMTK 测试程序,GitBash 恢复命令行模式,即可继续操作。
- Conquest DICOM Server 添加 SSL 双向认证后,出现 SSL_read 阻塞现象,整个线程卡死且端口长时间占用无法释放。 
 服务器采用 select + block socket 模型,通常情况下不会阻塞,抓包发现服务端发出的应答包丢失,客户端超时重发仍无法收到确认包的情况下发送 RST 报文,但服务端阻塞线程依然无法退出,线程僵死且处于半连接状态。
 初步确认为,OpenSSL 记录协议,需要读取到完整的记录才能解密,才认为 socket 读取完毕,否则一致阻塞。
- OpenSSL 自签名证书在 Chrome 下报告 Subject Alternative Name Missing 错误 - 1 
 2
 3
 4- Certificate - Subject Alternative Name missing 
 The certificate for this site does not contain a Subject Alternative Name extension containing a domain name or IP address.
 Certificate - missing
 This site is missing a valid, trusted certificate (net::ERR_CERT_COMMON_NAME_INVALID).- 处理方法:生成证书时加入参数 -sha256 -extfile v3.ext。 
 v3.ext 内容如下: (DOMAIN 替换为域名)- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10- authorityKeyIdentifier=keyid,issuer 
 basicConstraints=CA:FALSE
 keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
 subjectAltName = @alt_names
 [alt_names]
 DNS.1 = %%DOMAIN%%
 DNS.2 = %%DOMAIN%%
 IP.1 = %%IP%%
 IP.2 = %%IP%%
- OpenSSL 的扩展用途: - 1 
 2
 3
 4
 5
 6- Client Authentication --- 1.3.6.1.5.5.7.3.2 
 Server Authentication --- 1.3.6.1.5.5.7.3.1
 Secure Email ------------ 1.3.6.1.5.5.7.3.4
 Code Signing ------------ 1.3.6.1.5.5.7.3.3
 Timestamp Signing ------- 1.3.6.1.5.5.7.3.8
 // 更多参考:https://oidref.com/1.3.6.1.5.5.7.3.2
- 关于本地调试遇到的问题: 
 - net::ERR_CERT_COMMON_NAME_INVALID
 - net::ERR_CERT_DATE_INVALID
 处理方法:COMMON_NAME 代表证书中的服务器域名,使用正确的域名(生成签名请求时指定 Common Name 或者在 extfile 中添加 Subject Alternative Name, 本地环境建议加入 localhost);DATE INVALID 是指证书过期了,openssl x509 检查日期,注意,-days 参数值竟然可以是负数(时间往前推)。
- OpenSSL 查看代码签名文件信息 
 windows 代码签名文件扩展名为 .cer,属于 DER 编码格式:- openssl.exe x509 -inform DER -text -noout -in sign.cer
参考网址
基于 OpenSSL 自建 CA 和颁发 SSL 证书
google-chrome – Chrome:无效的自签名SSL证书 – “Subject Alternative Name Missing”
OpenSSL自签发配置有多域名或ip地址的证书
https wireshark抓包——要解密出原始数据光有ssl 证书还不行,还要有浏览器内的pre-master-secret(内存里)
RSA与 Diffie-Hellman密钥交换 的区别
SSL/TLS协议详解(下)——TLS握手协议