OpenSSL 生成证书的流程

Table of Contents

该笔记记录了利用 OpenSSL 工具集生成自签名证书、证书链的过程
下面会记录两种生成证书的路径,
第一种复杂,且功能稍有些缺失,利用的工具为证书相关的,主要是用来记录证书生成的过程,方便理解。
第二种简单,且有附加的功能,利用的工具为 CA 相关的,比较具有实战意义。

术语简介 & 原理简介

RSA 算法

RSA 是一种加密 & 解密算法。
RSA 有两组平行的概念,这里请注意区分:

  • 从 RSA 生成密钥对的概念上来说

    • RSA 私钥:RSA 算法首先生成的密钥,一般来说由生成的人自己持有,不公开

    • RSA 公钥:用 RSA 私钥简单推导后生成的密钥,该密钥可以公开发送给外部。

RSA 私钥能简单的推导出 RSA 公钥,但 RSA 公钥想要推导出 RSA 私钥非常复杂(几乎不能完成)。

  • 从 RSA 密钥使用的方法上来说

    • RSA 加密密钥:用于将明文加密为密文的密钥

    • RSA 解密密钥:用于将密文解密为明文的密钥

常见的加密/解密算法会使用相同的密钥作为加密/解密密钥,但是,
RSA 的算法特殊的地方就在于:

  • 从 加密/解密密钥的角度来说

    • RSA 的 加密密钥 和 解密密钥 并不相同

    • RSA 的 加密密钥 和 解密密钥 一一对应(成对出现)

    • 也就是说由 加密密钥 加密的数据,仅能由对应的 解密密钥 解密。

  • 从 公钥/私钥的角度来说

    • 公钥 和 私钥 都可以作为 加密密钥 和 解密密钥

    • 一对公钥/私钥互为加解密密钥

    • 也就是说,用公钥加密的数据仅能对应的私钥解密,而用私钥加密的数据仅能对应的公钥解密。

于是,这样来说,RSA 算法有两种用途:

  1. 信息加密
    事先获得接收方的公钥,用公钥加密明文数据,此时仅有持有私钥的人才能将密文数据解密为明文。

  2. 信息签名(证明发送身份)
    事先获得发送方的公钥,要求信息发送方加密一段明文已知的信息,若信息接收方能用发送方的公钥解开,并获得正确的明文,则证明信息的确为发送方发送的。

其他术语的简单说明

HTTPS超文本传输安全协议,一种使用了 TLS 的传输协议,网址中以 https:// 开头的网站一般可以认为启用的 HTTPS。
这些网站通过 TLS 和相关的协议向浏览器证明自己的身份。
TLS传输层安全协议,一种用于网络数据传输中保证数据安全(不泄密、不被篡改)的协议。其底层算法包含了 RSA 算法。
TLS 证书在 TLS 中用于证明服务器身份的数据,这些数据一般以文件的形式存放在服务器上,在客户端访问服务器的时候,由服务器发送给客户端,客户端会通过一些方法验证该证书的有效性,从而验证服务器的有效性。
X509一种证书的格式的协议,该协议指定了证书该包含什么内容。
X509 V3X509 证书的扩展,也就是向 X509 证书中追加一些数据,提供一些额外的信息。
CA证书的发放机构/发放者,一般来说,CA 也会管理所发放证书的有效性。
PKI公开密钥基础建设,围绕证书的产生、使用、销毁的软件、硬件、人员、策略等的基础构架。
DER一种编码,指出证书如何用二进制方式表示(在硬盘上存储,或是在网络中传输)。
PEM用 base64 编码的 DER 编码的证书。
OpenSSL一套开源软件,可以用于生成、管理、验证、吊销证书。

用复杂的方法,利用 OpenSSL 生成证书

 

Warning除了配置文件为明文文本文件以外,其余文件均为 PEM 编码的文件。

生成自签名根证书

  1. 生成根 CA 所需要的密钥

    	// 使用 genrsa 伪命令生成了 4096 位的 RSA 私钥
    	openssl genrsa -out my_rootCA.prikey 4096

  2. 【可选】检查私钥

    	// 以可读文本的形式输出当前私钥的信息
    	openssl rsa -in my_rootCA.prikey -noout -text

  3. 书写证书请求的配置文件
    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

  4. 生成证书请求

    	// 用 req 伪命令生成证书请求
    	openssl req -config my_rootCA.cnf -new -key my_rootCA.prikey -out my_rootCA.csr

  5. 【可选】检查证书请求

    	// 虽然检查证书请求理论上不需要配置文件
    	// 但 OpenSSL 工具集似乎需要一个可以正常读取的配置文件才能运行该命令
    	openssl req -config my_rootCA.cnf -in my_rootCA.csr -noout -text

  6. 书写证书签名配置文件
    与证书请求的配置文件类似,自签名证书的配置文件仅需在证书请求配置文件上稍加修改即可
    my_rootCA.cnf

    	...
    	[req]
    	...
    	x509_extensions = v3_rootCA # 指定证书要读取的扩展段
    	...
    	[v3_rootCA]
    	...
    	authorityKeyIdentifier = keyid, issuer  # 自处指定要记录下授权者/签发者的密钥标识符

  7. 生成自签名证书

    	// 用 req 伪命令生成了一个有效期 365 天的自签名 CA 证书
    	openssl req -config my_rootCA.cnf -x509 -days 365 -in my_rootCA.csr -key my_rootCA.prikey -out my_rootCA.crt

  8. 【可选】检查证书

    	openssl x509 -in my_rootCA.crt -noout -text

【可选】生成中间 CA 证书

根 CA 的信息一般需要离线存储,以保证安全,但为其他证书签名又需要使用这些信息,所以可以用根证书签发一个中间 CA 证书,用中间 CA 证书为终点证书签名,若中间证出现问题,将中间 CA 证书加入证书吊销列表中,重新用根证书签发新的中间 CA 证书即可。

Warning生成根证书时所执行的检查命令下方命令均可以使用,所以省略
  1. 生成中间 CA 证书的密钥

    	openssl genrsa -out my_midCA.prikey 4096

  2. 书写中间 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

  3. 生成证书请求

    	openssl req -config my_midCA.cnf -new -key my_midCA.prikey -out my_midCA.csr

  4. 书写证书生成配置

    Warning这个文件一般由签发者负责管理和使用

    my_midCA_v3_cert.cnf
    	[midCA_v3_cert]
    	basicConstraints = critical, CA:TRUE, pathlen:0
    	subjectKeyIdentifier = hash
    	authorityKeyIdentifier = keyid, issuer

  5. 用根证书为中间证书签名

    Warning1. 我们使用了 -CAcreateserial \`这个参数,命令会在根证书目录下放置一个 \`my_rootCA.srl 的文件,用来下次将要使用的序列号
    2. 若已经存在序列号文件,那么建议-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

生成终点(End Point)证书

终点证书作为证书链条的结尾,被实际用于验证某个网站、程序等的身份。

  1. 生成终点证书私钥

    	openssl genrsa -out my_site.prikey 2048 # 这里使用了 2048 位的私钥,在安全和执行效率之间取得一个平衡值

  2. 书写终点证书的证书请求配置文件
    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

  3. 生成证书请求

    	openssl req -config my_site.cnf -new -key my_site.prikey -out my_site.csr

  4. 书写终点证书的签名配置文件

    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

  5. 用中间证书签发终点证书

    	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

用简单的方法,利用 OpenSSL 生成证书

【可选】启用 UTF8 支持

  1. 对于默认不支持 UTF8 的 Shell/Termial 其中 UTF8 支持

    • Windows 10:
      控制面板 时钟和区域 更改日期、时间或数字格式 管理 更改系统区域设置(C)…​ Beta版:使用 Unicode UTF-8 提供全球语言支持(U) 重启计算机

    • Debian:

      		dpkg-reconfigure locales

  2. 若启用了 UTF8 支持,则所有的 OpenSSL 配置文件必须以 UTF8 的格式存储

创建自签名根证书

  1. 建立根证书所需要的目录、文件

    	# 存放根证书私钥、根证书请求、根证书的目录
    	mkdir rootCA_certs
    	# 存放根证书请求配置文件、根证书自签名配置文件、中间证书签名配置文件的目录
    	mkdir rootCA_cnf
    	# 存放根 CA 数据库的目录
    	mkdir rootCA_db
    	# 创建一个新的根 CA 数据库文件
    	touch rootCA_db/rootCA_db.txt

  2. 书写根证书请求的配置文件
    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

  3. 生成根证书私钥和签名请求文件

    	openssl req -config rootCA_cnf/ReqRootCA.cnf -newkey rsa -out rootCA_certs/rootCA.csr

  4. 【可选】检查根证书请求文件

    	# 让 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"

  5. 书写根证书自签名,以及根 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

  6. 自签名根证书,并生成根 CA 的数据库

    	openssl ca -batch -config rootCA_cnf/SelfsignRootCA.cnf -selfsign -in rootCA_certs/rootCA.csr -notext -out rootCA_certs/rootCA.crt

  7. 【可选】检查根证书文件

    	# 让 openssl 支持 UTF8 输出证书内容
    	openssl x509 -in rootCA_certs/rootCA.crt -noout -text -nameopt "utf8, esc_ctrl, sep_multiline, space_eq, lname, align"

创建中间证书

  1. 建立中间证书所需要的目录、文件

    # 存放中间证书私钥、中间证书请求、中间证书的目录
    mkdir midCA_certs
    # 存放中间证书的请求配置文件、终点证书的签名配置文件的目录
    mkdir midCA_cnf
    # 存放中间 CA 数据库的目录
    mkdir midCA_db
    # 创建一个新的中间 CA 数据库文件
    touch midCA_db/midCA_db.txt

  2. 书写中间证书请求的配置文件
    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

  3. 生成中间证书私钥和签名请求文件

    openssl req -config midCA_cnf/ReqMidCA.cnf -newkey rsa -out midCA_certs/midCA.csr

  4. 书写中间证书的签名配置文件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

  5. 用根 CA 签名中间证书

    openssl ca -batch -config rootCA_cnf/SignMidCA.cnf -in midCA_certs/midCA.csr -notext -out midCA_certs/midCA.crt

创建终点证书

  1. 建立终点证书所需要的目录、文件

    # 存放终点证书私钥、终点证书请求、终点证书的目录
    mkdir site_certs
    # 存放终点证书请求的配置文件
    mkdir site_cnf

  2. 书写终点证书请求的配置文件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

  3. 生成终点证书私钥和签名请求文件

    openssl req -config site_cnf/ReqSite.cnf -newkey rsa -out site_certs/site.csr

  4. 书写终点证书的签名配置文件
    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

  5. 用中间 CA 签名终点证书

openssl ca -batch -config midCA_cnf/SignSite.cnf -in site_certs/site.csr -notext -out site_certs/site.crt

证书的使用

在使用时,需要将 rootCA.crt 添加至当前用户的 受信任的根证书颁发机构 中,midCA.crt 添加至当前用户的 中间证书颁发机构,将 my_site.crt 依照 web 服务程序的要求添加至站点中。即可完成网站的 https 启用。

作者:SteveChen  创建时间:2025-04-06 13:56
最后编辑:SteveChen  更新时间:2025-04-06 13:59
上一篇:
下一篇: