Skip to content

Latest commit

 

History

History
715 lines (608 loc) · 20.8 KB

86.0、openssl 证书签名流程.adoc

File metadata and controls

715 lines (608 loc) · 20.8 KB

OpenSSL 生成证书的流程

该笔记记录了利用 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 V3

X509 证书的扩展,也就是向 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. 用根证书为中间证书签名

    Warning
    1. 我们使用了 -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 启用。