该笔记记录了利用 OpenSSL 工具集生成自签名证书、证书链的过程
下面会记录两种生成证书的路径,
第一种复杂,且功能稍有些缺失,利用的工具为证书相关的,主要是用来记录证书生成的过程,方便理解。
第二种简单,且有附加的功能,利用的工具为 CA 相关的,比较具有实战意义。
RSA 是一种加密 & 解密算法。
RSA 有两组平行的概念,这里请注意区分:
-
从 RSA 生成密钥对的概念上来说
-
RSA 私钥:RSA 算法首先生成的密钥,一般来说由生成的人自己持有,不公开
-
RSA 公钥:用 RSA 私钥简单推导后生成的密钥,该密钥可以公开发送给外部。
-
RSA 私钥能简单的推导出 RSA 公钥,但 RSA 公钥想要推导出 RSA 私钥非常复杂(几乎不能完成)。
-
从 RSA 密钥使用的方法上来说
-
RSA 加密密钥:用于将明文加密为密文的密钥
-
RSA 解密密钥:用于将密文解密为明文的密钥
-
常见的加密/解密算法会使用相同的密钥作为加密/解密密钥,但是,
RSA 的算法特殊的地方就在于:
-
从 加密/解密密钥的角度来说
-
RSA 的 加密密钥 和 解密密钥 并不相同
-
RSA 的 加密密钥 和 解密密钥 一一对应(成对出现)
-
也就是说由 加密密钥 加密的数据,仅能由对应的 解密密钥 解密。
-
-
从 公钥/私钥的角度来说
-
公钥 和 私钥 都可以作为 加密密钥 和 解密密钥
-
一对公钥/私钥互为加解密密钥
-
也就是说,用公钥加密的数据仅能由对应的私钥解密,而用私钥加密的数据仅能由对应的公钥解密。
-
于是,这样来说,RSA 算法有两种用途:
-
信息加密
事先获得接收方的公钥,用公钥加密明文数据,此时仅有持有私钥的人才能将密文数据解密为明文。 -
信息签名(证明发送身份)
事先获得发送方的公钥,要求信息发送方加密一段明文已知的信息,若信息接收方能用发送方的公钥解开,并获得正确的明文,则证明信息的确为发送方发送的。
- HTTPS
-
超文本传输安全协议,一种使用了 TLS 的传输协议,网址中以
https://
开头的网站一般可以认为启用的 HTTPS。
这些网站通过 TLS 和相关的协议向浏览器证明自己的身份。 - TLS
-
传输层安全协议,一种用于网络数据传输中保证数据安全(不泄密、不被篡改)的协议。其底层算法包含了 RSA 算法。
- TLS 证书
-
在 TLS 中用于证明服务器身份的数据,这些数据一般以文件的形式存放在服务器上,在客户端访问服务器的时候,由服务器发送给客户端,客户端会通过一些方法验证该证书的有效性,从而验证服务器的有效性。
- X509
-
一种证书的格式的协议,该协议指定了证书该包含什么内容。
- X509 V3
-
X509 证书的扩展,也就是向 X509 证书中追加一些数据,提供一些额外的信息。
- CA
-
证书的发放机构/发放者,一般来说,CA 也会管理所发放证书的有效性。
- PKI
-
公开密钥基础建设,围绕证书的产生、使用、销毁的软件、硬件、人员、策略等的基础构架。
- DER
-
一种编码,指出证书如何用二进制方式表示(在硬盘上存储,或是在网络中传输)。
- PEM
-
用 base64 编码的 DER 编码的证书。
- OpenSSL
-
一套开源软件,可以用于生成、管理、验证、吊销证书。
Warning
|
除了配置文件为明文文本文件以外,其余文件均为 PEM 编码的文件。 |
-
生成根 CA 所需要的密钥
// 使用 genrsa 伪命令生成了 4096 位的 RSA 私钥 openssl genrsa -out my_rootCA.prikey 4096
-
【可选】检查私钥
// 以可读文本的形式输出当前私钥的信息 openssl rsa -in my_rootCA.prikey -noout -text
-
书写证书请求的配置文件
my_rootCA.cnf# req 伪命令会读取的配置段 [req] prompt = no # 禁用交互式输入,仅使用配置文件中的数据生成证书请求 distinguished_name = rootCA_dn # 可辨识名的段名称,该段的内容用于确定证书的主体 req_extensions = v3_rootCA # 扩展的段名称,该段用于为证书请求追加更多信息 # 证书主体的可辨识名,用于确定证书主体的唯一性 # 如同地址一样,写的越细致,发生同名碰撞的可能性就越低 [rootCA_dn] countryName = CN # 两字母表示的国家名 stateOrProvinceName = Beijing # 省名或州名 localityName = Beijing # 地区名(城市名) organizationName = myself # 组织名(公司名) organizationalUnitName = my root CA unit # 组织单元名(部门名) commonName = my root CA # 常见名 # 根证书扩展 [v3_rootCA] basicConstraints = critical, CA:TRUE # 基本约束,关键性约束,将证书设置为 CA subjectKeyIdentifier=hash # 用散列算法为主体的密钥创建标识符 # 这里不写 authorityKeyIdentifier,因为在生成请求阶段,还不知道授权者 # authorityKeyIdentifier = keyid, issuer
-
生成证书请求
// 用 req 伪命令生成证书请求 openssl req -config my_rootCA.cnf -new -key my_rootCA.prikey -out my_rootCA.csr
-
【可选】检查证书请求
// 虽然检查证书请求理论上不需要配置文件 // 但 OpenSSL 工具集似乎需要一个可以正常读取的配置文件才能运行该命令 openssl req -config my_rootCA.cnf -in my_rootCA.csr -noout -text
-
书写证书签名配置文件
与证书请求的配置文件类似,自签名证书的配置文件仅需在证书请求配置文件上稍加修改即可my_rootCA.cnf... [req] ... x509_extensions = v3_rootCA # 指定证书要读取的扩展段 ... [v3_rootCA] ... authorityKeyIdentifier = keyid, issuer # 自处指定要记录下授权者/签发者的密钥标识符
-
生成自签名证书
// 用 req 伪命令生成了一个有效期 365 天的自签名 CA 证书 openssl req -config my_rootCA.cnf -x509 -days 365 -in my_rootCA.csr -key my_rootCA.prikey -out my_rootCA.crt
-
【可选】检查证书
openssl x509 -in my_rootCA.crt -noout -text
根 CA 的信息一般需要离线存储,以保证安全,但为其他证书签名又需要使用这些信息,所以可以用根证书签发一个中间 CA 证书,用中间 CA 证书为终点证书签名,若中间证出现问题,将中间 CA 证书加入证书吊销列表中,重新用根证书签发新的中间 CA 证书即可。
Warning
|
生成根证书时所执行的检查命令下方命令均可以使用,所以省略 |
-
生成中间 CA 证书的密钥
openssl genrsa -out my_midCA.prikey 4096
-
书写中间 CA 的证书请求配置文件:
my_midCA.cnf# 该文件的说明参见根证书请求配置文件的说明 [req] prompt = no distinguished_name = midCA_dn req_extensions = v3_midCA [midCA_dn] countryName = CN stateOrProvinceName = Beijing localityName = Beijing organizationName = myself organizationalUnitName = my middle CA unit commonName = my middle CA [v3_midCA] # 注意,这里追加了 pathlen:0 这个参数,表示该证书下级能够存在的 CA 证书的的数量为 0,也就是说,该证书仅能签发终端证书 basicConstraints = critical, CA:TRUE, pathlen:0 subjectKeyIdentifier = hash
-
生成证书请求
openssl req -config my_midCA.cnf -new -key my_midCA.prikey -out my_midCA.csr
-
书写证书生成配置
Warning这个文件一般由签发者负责管理和使用
my_midCA_v3_cert.cnf[midCA_v3_cert] basicConstraints = critical, CA:TRUE, pathlen:0 subjectKeyIdentifier = hash authorityKeyIdentifier = keyid, issuer
-
用根证书为中间证书签名
Warning-
我们使用了
-CAcreateserial `这个参数,命令会在根证书目录下放置一个 `my_rootCA.srl
的文件,用来下次将要使用的序列号 -
若已经存在序列号文件,那么建议用
-CAserial <序列号文件>
把序列号列表文件引入命令
// 由于 x509 伪命令并不知道向何处查找扩展,所以这里要指定包含扩展的文件,以及扩展的段的名称 openssl x509 -req -days 90 -in my_midCA.csr -CA my_rootCA.crt -CAkey my_rootCA.prikey -CAcreateserial -extfile my_midCA_v3_cert.cnf -extensions midCA_v3_cert -out my_midCA.crt
-
终点证书作为证书链条的结尾,被实际用于验证某个网站、程序等的身份。
-
生成终点证书私钥
openssl genrsa -out my_site.prikey 2048 # 这里使用了 2048 位的私钥,在安全和执行效率之间取得一个平衡值
-
书写终点证书的证书请求配置文件
my_site.cnf[req] prompt = no distinguished_name = site_dn req_extensions = v3_site [site_dn] countryName = CN stateOrProvinceName = Beijing localityName = Beijing organizationName = myself organizationalUnitName = my site unit commonName = my site [v3_site] basicConstraints = critical, CA:FALSE # 用 CA:FALSE 表示该证书为终点证书,不能用于签发其他证书 extendedKeyUsage = critical, serverAuth # 指定 serverAuth 将证书的使用范围进一步限制为服务器身份验证 subjectKeyIdentifier = hash subjectAltName = @alt_name # 利用主体替代名段来追加可以被该证书认证的其他名字(比如网站的 DNS 名,IP 地址等等) [alt_name] DNS.1 = mysite.mydomain # 声明该证书可以验证本站的顶层域名 DNS.2 = *.mysite.mydomain # 声明该证书可以验证本站顶层域名下的子域名(不包含子域名的子域名 eg. subsub.sub.mysite.mydomain) IP.1 = 127.0.0.1 # 声明该证书可以验证 IP 地址 1 IP.2 = 127.0.0.2 # 声明该证书可以验证 IP 地址 2
-
生成证书请求
openssl req -config my_site.cnf -new -key my_site.prikey -out my_site.csr
-
书写终点证书的签名配置文件
Warning下面我们要使用 x509 伪命令生成终点证书,
x509 并非完全的 ca 工具,并不能从证书请求中提取全部的信息,这里需要手动补充一些信息
使用 ca 伪命令时有其他的替代方案。my_site_v3_cert.cnf[site_v3_cert] basicConstraints = critical, CA:FALSE extendedKeyUsage = critical, serverAuth subjectKeyIdentifier = hash authorityKeyIdentifier = keyid, issuer subjectAltName = @alt_name [alt_name] DNS.1 = mysite.mydomain DNS.2 = *.mysite.mydomain IP.1 = 127.0.0.1 IP.2 = 127.0.0.2
-
用中间证书签发终点证书
openssl x509 -req -days 90 -in my_site.csr -CA my_midCA.crt -CAkey my_midCA.prikey -CAcreateserial -extfile my_site_v3_cert.cnf -extensions site_v3_cert -out my_site.crt
至此,我们就获得了最终的终点证书 my_site.crt
-
对于默认不支持 UTF8 的 Shell/Termial 其中 UTF8 支持
-
Windows 10:
控制面板 › 时钟和区域 › 更改日期、时间或数字格式 › 管理 › 更改系统区域设置(C)… › Beta版:使用 Unicode UTF-8 提供全球语言支持(U) › 重启计算机 -
Debian:
dpkg-reconfigure locales
-
-
若启用了 UTF8 支持,则所有的 OpenSSL 配置文件必须以 UTF8 的格式存储
-
建立根证书所需要的目录、文件
# 存放根证书私钥、根证书请求、根证书的目录 mkdir rootCA_certs # 存放根证书请求配置文件、根证书自签名配置文件、中间证书签名配置文件的目录 mkdir rootCA_cnf # 存放根 CA 数据库的目录 mkdir rootCA_db # 创建一个新的根 CA 数据库文件 touch rootCA_db/rootCA_db.txt
-
书写根证书请求的配置文件
rootCA_cnf/ReqRootCA.cnf[req] # 设置私钥的加密密码 output_password = my root password default_bits = 4096 default_keyfile = rootCA_certs/rootCA.prikey req_extensions = v3_req prompt = no # 启用 UTF8 支持 utf8 = yes distinguished_name = root_dn [root_dn] # 由于启用了 UTF8,这里我们可以输入中文 commonName = 我的测试根证书签发机构 countryName = CN stateOrProvinceName = 北京 localityName = 北京 organizationName = 我自己的组织 organizationalUnitName = 我自己的组织的证书签发单元 # 根据 ca 伪命令配置文件的建议,不在 DN 中包含 email 地址 [v3_req] basicConstraints = critical, CA:TRUE # 在这里补充邮件地址 subjectAltName = dirName:alt_dir [alt_dir] emailAddress = myaddress@mydomain
-
生成根证书私钥和签名请求文件
openssl req -config rootCA_cnf/ReqRootCA.cnf -newkey rsa -out rootCA_certs/rootCA.csr
-
【可选】检查根证书请求文件
# 让 openssl 支持 UTF8 输出证书内容 openssl req -config rootCA_cnf/ReqRootCA.cnf -in rootCA_certs/rootCA.csr -noout -text -nameopt "utf8, esc_ctrl, sep_multiline, space_eq, lname, align"
-
书写根证书自签名,以及根 CA 数据库的配置文件
rootCA_cnf/SelfsignRootCA.cnf[ca] default_ca = rootCA [rootCA] # 新证书的存储目录 new_certs_dir = ./rootCA_certs # 用于签名的私钥 private_key = rootCA_certs/rootCA.prikey # 默认签名函数 default_md = sha256 # 默认证书有效天数 default_days = 3650 # CA 数据库文件 database = rootCA_db/rootCA_db.txt # 设置主体不唯一 unique_subject = no # 该选项帮助文档中未出现,但可参见 -rand_serial 命令行参数了解 rand_serial = yes # x509 扩展段 x509_extensions = v3_x509 # 保留证书请求中的 DN preserve = yes # 从 DN 中移除电子邮件地址 email_in_dn = no # 从证书请求中拷贝该配置文件中未指定的扩展 copy_extensions = copy # 策略段 policy = cert_policy # 为了支持 UTF8 输出而做的修改 name_opt = utf8, esc_ctrl, sep_multiline, space_eq, lname, align [v3_x509] basicConstraints = critical, CA:TRUE # 该证书中的密钥可以用于数字签名、签名证书、签名吊销列表 keyUsage = digitalSignature, keyCertSign, cRLSign # 该证书中的密钥也可以也可用于 OCSP 签名 extendedKeyUsage = OCSPSigning subjectKeyIdentifier = hash authorityKeyIdentifier = keyid, issuer [cert_policy] countryName = supplied stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional
-
自签名根证书,并生成根 CA 的数据库
openssl ca -batch -config rootCA_cnf/SelfsignRootCA.cnf -selfsign -in rootCA_certs/rootCA.csr -notext -out rootCA_certs/rootCA.crt
-
【可选】检查根证书文件
# 让 openssl 支持 UTF8 输出证书内容 openssl x509 -in rootCA_certs/rootCA.crt -noout -text -nameopt "utf8, esc_ctrl, sep_multiline, space_eq, lname, align"
-
建立中间证书所需要的目录、文件
# 存放中间证书私钥、中间证书请求、中间证书的目录 mkdir midCA_certs # 存放中间证书的请求配置文件、终点证书的签名配置文件的目录 mkdir midCA_cnf # 存放中间 CA 数据库的目录 mkdir midCA_db # 创建一个新的中间 CA 数据库文件 touch midCA_db/midCA_db.txt
-
书写中间证书请求的配置文件
midCA_cnf/ReqMidCA.cnf[req] output_password = my mid password default_bits = 2048 default_keyfile = midCA_certs/midCA.prikey req_extensions = v3_req prompt = no utf8 = yes distinguished_name = mid_dn [mid_dn] commonName = 我的测试中间证书签发机构 countryName = CN stateOrProvinceName = 北京 localityName = 北京 organizationName = 我自己的组织 organizationalUnitName = 我自己的组织的证书签发单元 [v3_req] # 限制用该配置文件生成的证书请求,对应的证书,仅能用于签名终点证书 basicConstraints = critical, CA:TRUE, pathlen:0 subjectAltName = dirName:alt_dir [alt_dir] emailAddress = myaddress@mydomain
-
生成中间证书私钥和签名请求文件
openssl req -config midCA_cnf/ReqMidCA.cnf -newkey rsa -out midCA_certs/midCA.csr
-
书写中间证书的签名配置文件
rootCA_cnf/SignMidCA.cnf[ca] default_ca = midCA [midCA] new_certs_dir = ./midCA_certs private_key = rootCA_certs/rootCA.prikey # 用于签名的 CA 证书 certificate = rootCA_certs/rootCA.crt default_md = sha256 default_days = 365 database = rootCA_db/rootCA_db.txt unique_subject = no rand_serial = yes x509_extensions = v3_x509 preserve = yes email_in_dn = no copy_extensions = copy policy = cert_policy name_opt = utf8, esc_ctrl, sep_multiline, space_eq, lname, align [v3_x509] # 限制用该配置文件签名出的证书,仅能用于签名终点证书 basicConstraints = critical, CA:TRUE, pathlen:0 keyUsage = digitalSignature, keyCertSign, cRLSign extendedKeyUsage = OCSPSigning subjectKeyIdentifier = hash authorityKeyIdentifier = keyid, issuer [cert_policy] countryName = supplied stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional
-
用根 CA 签名中间证书
openssl ca -batch -config rootCA_cnf/SignMidCA.cnf -in midCA_certs/midCA.csr -notext -out midCA_certs/midCA.crt
-
建立终点证书所需要的目录、文件
# 存放终点证书私钥、终点证书请求、终点证书的目录 mkdir site_certs # 存放终点证书请求的配置文件 mkdir site_cnf
-
书写终点证书请求的配置文件
site_cnf/ReqSite.cnf[req] output_password = my site password default_bits = 2048 default_keyfile = site_certs/site.prikey req_extensions = v3_req prompt = no utf8 = yes distinguished_name = site_dn [site_dn] commonName = 我的测试站点证书 countryName = CN stateOrProvinceName = 北京 localityName = 北京 organizationName = 我自己的组织 organizationalUnitName = 我自己的组织的站点单元 [v3_req] # 限制用该配置文件生成的证书请求,对应的证书,不能用于签名其它证书 basicConstraints = critical, CA:FALSE subjectAltName = @alt_name [alt_name] # 在这里追加要验证的域名、IP 地址等 DNS.1 = mysite.mydomain DNS.2 = *.mysite.mydomain IP.1 = 127.0.0.1 IP.2 = 127.0.0.2 dirName = alt_dir [alt_dir] emailAddress = myaddress@mydomain
-
生成终点证书私钥和签名请求文件
openssl req -config site_cnf/ReqSite.cnf -newkey rsa -out site_certs/site.csr
-
书写终点证书的签名配置文件
midCA_cnf/SignSite.cnf[ca] default_ca = site [site] new_certs_dir = ./site_certs private_key = midCA_certs/midCA.prikey certificate = midCA_certs/midCA.crt default_md = sha256 default_days = 90 database = midCA_db/midCA_db.txt unique_subject = no rand_serial = yes x509_extensions = v3_x509 preserve = yes email_in_dn = no copy_extensions = copy policy = cert_policy name_opt = utf8, esc_ctrl, sep_multiline, space_eq, lname, align [v3_x509] # 限制用该配置文件签名出的证书,不能用于签名其它证书 basicConstraints = critical, CA:FALSE # 该证书中的密钥用于数字签名、加密私钥、加密用户数据、 keyUsage = digitalSignature, keyEncipherment, dataEncipherment # 该证书中的密钥还可以用于服务器验证 extendedKeyUsage = serverAuth subjectKeyIdentifier = hash authorityKeyIdentifier = keyid, issuer [cert_policy] countryName = supplied stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional
-
用中间 CA 签名终点证书
openssl ca -batch -config midCA_cnf/SignSite.cnf -in site_certs/site.csr -notext -out site_certs/site.crt