用OpenSSL建立ECDSA (ECC) CA

本文中所用主要方法,取自https://jamielinux.com/docs/openssl-certificate-authority/ 但有所修改.

用于管理CA的系统安装于虚拟机,配置如下:

  1. Ubuntu 16.04.3 LTS(Server)
    • 要启用OpenSSH-Server,即sshd,以在主机上透过SSH访问
    • 需要OpenSSL,可以使用自带的1.0.2g版本,我使用的是自行编译的1.1.0f版本,故下文所涉及的椭圆曲线列表可能有差异
    • 本文中一切在用于CA主机上的操作均使用root用户
  2. 单核心处理器
  3. 256MiB内存
  4. 8GiB硬盘
  5. 直连模式的虚拟网卡
    • 必须将虚拟网卡的模式由"NAT"改为"直接连接",以保证能从主机上透过SSH访问该虚拟机)
建立CA

1.建立根证书目录

$ cd ~
$ mkdir EC_CA
$ cd EC_CA
$ mkdir crl newcerts
$ touch index.txt
$ echo "0000" > serial

2.在~/EC_CA/目录下建立OpenSSL配置文件,命名为openssl.cnf,内容如下

[ ca ]
default_ca = CA_default

[ CA_default ]
# 路径根据实际情况修改
dir               = /root/EC_CA
crl_dir           = $dir/crl
database          = $dir/index.txt
serial            = $dir/serial
RANDFILE          = $dir/.rand
new_certs_dir     = $dir/newcerts
private_key       = $dir/ca.key
certificate       = $dir/ca.cer
crlnumber         = $dir/crlnumber
crl               = $dir/crl/ca.crl
crl_extensions    = crl_ext
default_crl_days  = 10
default_md        = sha384

name_opt          = ca_default
cert_opt          = ca_default
default_days      = 365
preserve          = no
policy            = policy_loose

[ policy_strict ]
# 严格政策,在为中级CA签名时使用
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ policy_loose ]
# 宽松政策,在为用户/服务器证书签名时使用
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
distinguished_name  = req_distinguished_name
string_mask         = utf8only

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
stateOrProvinceName             = State or Province Name
localityName                    = Locality Name
0.organizationName              = Organization Name
organizationalUnitName          = Organizational Unit Name
commonName                      = Common Name
emailAddress                    = Email Address
default_md          = sha256
x509_extensions     = v3_ca

[ v3_ca ]
# CA证书配置
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

[ usr_cert ]
# 用户证书配置
basicConstraints = CA:FALSE
nsCertType = client, email
nsComment = "Hardrain Users' Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection

[ server_cert ]
# 服务器证书配置
basicConstraints = CA:FALSE
nsCertType = server
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
#keyUsage = critical, digitalSignature, keyEncipherment
keyUsage = keyEncipherment
extendedKeyUsage = serverAuth
# 添加SAN支持
subjectAltName = @alt_names

[ crl_ext ]
authorityKeyIdentifier=keyid:always

[ ocsp ]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, OCSPSigning

# SubjectAlterName,可追加更多
[ alt_names ]
DNS.1 = domain.tld
DNS.2 = *.domain.tld

3.建立私钥
首先使用openssl ecparam -list_curves查看可用的Named curves列表
得到类似如下输出:

  secp112r1 : SECG/WTLS curve over a 112 bit prime field
  secp112r2 : SECG curve over a 112 bit prime field
  secp128r1 : SECG curve over a 128 bit prime field
  secp128r2 : SECG curve over a 128 bit prime field
  secp160k1 : SECG curve over a 160 bit prime field
  secp160r1 : SECG curve over a 160 bit prime field
  secp160r2 : SECG/WTLS curve over a 160 bit prime field
  secp192k1 : SECG curve over a 192 bit prime field
  secp224k1 : SECG curve over a 224 bit prime field
  secp224r1 : NIST/SECG curve over a 224 bit prime field
  secp256k1 : SECG curve over a 256 bit prime field
  secp384r1 : NIST/SECG curve over a 384 bit prime field
  secp521r1 : NIST/SECG curve over a 521 bit prime field
  prime192v1: NIST/X9.62/SECG curve over a 192 bit prime field
  prime192v2: X9.62 curve over a 192 bit prime field
  prime192v3: X9.62 curve over a 192 bit prime field
  prime239v1: X9.62 curve over a 239 bit prime field
  prime239v2: X9.62 curve over a 239 bit prime field
  prime239v3: X9.62 curve over a 239 bit prime field
  prime256v1: X9.62/SECG curve over a 256 bit prime field
  sect113r1 : SECG curve over a 113 bit binary field
  sect113r2 : SECG curve over a 113 bit binary field
  sect131r1 : SECG/WTLS curve over a 131 bit binary field
  sect131r2 : SECG curve over a 131 bit binary field
  sect163k1 : NIST/SECG/WTLS curve over a 163 bit binary field
  sect163r1 : SECG curve over a 163 bit binary field
  sect163r2 : NIST/SECG curve over a 163 bit binary field
  sect193r1 : SECG curve over a 193 bit binary field
  sect193r2 : SECG curve over a 193 bit binary field
  sect233k1 : NIST/SECG/WTLS curve over a 233 bit binary field
  sect233r1 : NIST/SECG/WTLS curve over a 233 bit binary field
  sect239k1 : SECG curve over a 239 bit binary field
  sect283k1 : NIST/SECG curve over a 283 bit binary field
  sect283r1 : NIST/SECG curve over a 283 bit binary field
  sect409k1 : NIST/SECG curve over a 409 bit binary field
  sect409r1 : NIST/SECG curve over a 409 bit binary field
  sect571k1 : NIST/SECG curve over a 571 bit binary field
  sect571r1 : NIST/SECG curve over a 571 bit binary field
  c2pnb163v1: X9.62 curve over a 163 bit binary field
  c2pnb163v2: X9.62 curve over a 163 bit binary field
  c2pnb163v3: X9.62 curve over a 163 bit binary field
  c2pnb176v1: X9.62 curve over a 176 bit binary field
  c2tnb191v1: X9.62 curve over a 191 bit binary field
  c2tnb191v2: X9.62 curve over a 191 bit binary field
  c2tnb191v3: X9.62 curve over a 191 bit binary field
  c2pnb208w1: X9.62 curve over a 208 bit binary field
  c2tnb239v1: X9.62 curve over a 239 bit binary field
  c2tnb239v2: X9.62 curve over a 239 bit binary field
  c2tnb239v3: X9.62 curve over a 239 bit binary field
  c2pnb272w1: X9.62 curve over a 272 bit binary field
  c2pnb304w1: X9.62 curve over a 304 bit binary field
  c2tnb359v1: X9.62 curve over a 359 bit binary field
  c2pnb368w1: X9.62 curve over a 368 bit binary field
  c2tnb431r1: X9.62 curve over a 431 bit binary field
  wap-wsg-idm-ecid-wtls1: WTLS curve over a 113 bit binary field
  wap-wsg-idm-ecid-wtls3: NIST/SECG/WTLS curve over a 163 bit binary field
  wap-wsg-idm-ecid-wtls4: SECG curve over a 113 bit binary field
  wap-wsg-idm-ecid-wtls5: X9.62 curve over a 163 bit binary field
  wap-wsg-idm-ecid-wtls6: SECG/WTLS curve over a 112 bit prime field
  wap-wsg-idm-ecid-wtls7: SECG/WTLS curve over a 160 bit prime field
  wap-wsg-idm-ecid-wtls8: WTLS curve over a 112 bit prime field
  wap-wsg-idm-ecid-wtls9: WTLS curve over a 160 bit prime field
  wap-wsg-idm-ecid-wtls10: NIST/SECG/WTLS curve over a 233 bit binary field
  wap-wsg-idm-ecid-wtls11: NIST/SECG/WTLS curve over a 233 bit binary field
  wap-wsg-idm-ecid-wtls12: WTLS curve over a 224 bit prime field
  Oakley-EC2N-3: 
	IPSec/IKE/Oakley curve #3 over a 155 bit binary field.
	Not suitable for ECDSA.
	Questionable extension field!
  Oakley-EC2N-4: 
	IPSec/IKE/Oakley curve #4 over a 185 bit binary field.
	Not suitable for ECDSA.
	Questionable extension field!
  brainpoolP160r1: RFC 5639 curve over a 160 bit prime field
  brainpoolP160t1: RFC 5639 curve over a 160 bit prime field
  brainpoolP192r1: RFC 5639 curve over a 192 bit prime field
  brainpoolP192t1: RFC 5639 curve over a 192 bit prime field
  brainpoolP224r1: RFC 5639 curve over a 224 bit prime field
  brainpoolP224t1: RFC 5639 curve over a 224 bit prime field
  brainpoolP256r1: RFC 5639 curve over a 256 bit prime field
  brainpoolP256t1: RFC 5639 curve over a 256 bit prime field
  brainpoolP320r1: RFC 5639 curve over a 320 bit prime field
  brainpoolP320t1: RFC 5639 curve over a 320 bit prime field
  brainpoolP384r1: RFC 5639 curve over a 384 bit prime field
  brainpoolP384t1: RFC 5639 curve over a 384 bit prime field
  brainpoolP512r1: RFC 5639 curve over a 512 bit prime field
  brainpoolP512t1: RFC 5639 curve over a 512 bit prime field

从中选择一种,如384位(等效于3072位RSA)的NIST P-384(secp384r1),来生成私钥并使用AES-256-CBC加密

# 这一步会要求输入加密密码.
$ openssl ecparam -name P-384 -genkey|openssl ec -aes256 -out ca.key
$ chmod 400 ca.key

4.生成CA证书

# 这一步需要输入证书相关信息(国家/地区/机构/部门/通用名/电邮)和上一步所设置的密码
# 有效期和摘要算法(此例为sha256)可酌情更改,根CA证书可以使用不安全的摘要算法,如SHA1/MD5.
$ openssl req -config openssl.cnf -key ca.key -new -x509 -days 3650 -sha256 -extensions v3_ca -out ca.crt
$ chmod 444 ca.crt

至此,这个CA已经建立完成了,在使用前须将ca.crt拷贝至所需设备上并添加至信任的CA.


使用CA来签名证书

以下步骤在用户一侧完成:
5.建立OpenSSL配置文件,命名为openssl_usr.cnf,内容如下:

[ req ]
default_bits        = 2048
distinguished_name  = req_distinguished_name
string_mask         = utf8only
req_extensions      = req_ext
# 必须使用安全的摘要算法(sha256|sha384|sha512)
default_md          = sha384

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
stateOrProvinceName             = State or Province Name
localityName                    = Locality Name
0.organizationName              = Organization Name
organizationalUnitName          = Organizational Unit Name
commonName                      = Common Name
emailAddress                    = Email Address

[ req_ext ]
# 若生成(用于SSL客户端认证/电子邮件加密的)用户证书,注释掉以下一行
subjectAltName = @alt_names

[ alt_names ]
# 服务器证书的SubjectAlterName,可追加
DNS.1 = domain.tld
DNS.2 = *.domain.tld

6.生成私钥,以2048位的RSA为例

$ openssl genrsa -out domain.tld.key 2048

7.生成CSR(Certificate Signing Request,证书签名请求)文件

// 必须使用安全的摘要算法
// 要填写证书相关信息(国家/地区/机构/部门/通用名/电邮)
// 如果私钥被加密,这一步需要输入密码
$ openssl req -config openssl_usr.cnf -key domain.tld.key -new -sha384 -out domain.tld.csr
// 检查CSR的信息是否正确
$ openssl req -in domain.tld.csr -noout -text

将生成的.csr文件提交给CA.


以下步骤在CA一侧完成
8.查看CSR信息,如为服务器证书,找到其所有SAN,填入CA的openssl.cnf(重要,如忽略此步骤,签名的服务器证书将因为没有任何SAN而无法使用)
然后对证书进行签名

$ cd ~/EC_CA/
// 此步骤将确认证书信息,并将签名事件写入日志(index.txt),由于使用到私钥,需输入第3步所设置的密码
$ openssl ca -config openssl.cnf -extensions server_cert -days 365 -notext -in ./domain.tld.csr -out ./newcerts/domain.tld.cer
// 验证证书信息
$ openssl x509 -in ./newcerts/domain.tld.cer -noout -text

将domain.tld.cer交给用户,即完成.