本文旨在利用开源openssl软件,在Linux(或UNIX/Cygwin)下创建一个简单的CA。我们可以利用这个CA进行PKI、数字证书相关的测试。比如,在测试用Tomcat或Apache构建HTTPS双向认证时,我们可以利用自己建立的测试CA来为服务器端颁发服务器数字证书,为客户端(浏览器)生成文件形式的数字证书(可以同时利用openssl生成客户端私钥)。
 
 该简单的CA将建立在用户自己的目录下($HOME/testca),无需超级用户(root)权限。
 
一. 创建CA
1. 创建CA需要用到的目录和文件:
执行命令如下:
mkdir "$HOME/testca"
cd "$HOME/testca"
mkdir newcerts private conf
chmod g-rwx,o-rwx private
echo "01" > serial
touch index.txt
 
说明:
$HOME/testca为待建CA的主目录。其中newcerts子目录将存放CA签署(颁发)过的数字证书(证书备份目录)。而private目录用于存放CA的私钥。目录conf只是用于存放一些简化参数用的配置文件。
 
文件serial和index.txt分别用于存放下一个证书的序列号和证书信息数据库。
当然,偷懒起见,可以只用按照本文操作即可,不一定需要关心各个目录和文件的作用。
 
2. 生成CA的私钥和自签名证书(即根证书)
创建文件:
vi "$HOME/testca/conf/gentestca.conf"
文件内容如下:
####################################
[ req ]
default_keyfile = $ENV::HOME/testca/private/cakey.pem
default_md = md5
prompt = no
distinguished_name = ca_distinguished_name
x509_extensions = ca_extensions
 
[ ca_distinguished_name ]
organizationName = TestOrg
organizationalUnitName  = TestDepartment
commonName = TestCA
emailAddress = ca_admin@testorg.com
 
[ ca_extensions ]
basicConstraints = CA:true
########################################
 
然后执行命令如下:
cd "$HOME/testca"
openssl req -x509 -newkey rsa:2048 -out cacert.pem -outform PEM -days 2190 -config "$HOME/testca/conf/gentestca.conf"
执行过程中需要输入CA私钥的保护密码,假设我们输入密码: 888888
 
可以用如下命令查看一下CA自己证书的内容
openssl x509 -in cacert.pem -text -noout
 
3. 创建一个配置文件,以便后续CA日常操作中使用:
vi "$HOME/testca/conf/testca.conf"
文件内容如下:
####################################
[ ca ]
default_ca      = testca                   # The default ca section
 
[ testca ]
dir            = $ENV::HOME/testca         # top dir
database       = $dir/index.txt          # index file.
new_certs_dir  = $dir/newcerts           # new certs dir
 
certificate    = $dir/cacert.pem         # The CA cert
serial         = $dir/serial             # serial no file
private_key    = $dir/private/cakey.pem  # CA private key
RANDFILE       = $dir/private/.rand      # random number file
 
default_days   = 365                     # how long to certify for
default_crl_days= 30                     # how long before next CRL
default_md     = md5                     # message digest method to use
unique_subject = no                      # Set to ‘no’ to allow creation of
                                         # several ctificates with same subject.
policy         = policy_any              # default policy
 
[ policy_any ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional
 
########################################
 
 
二. CA的日常操作
1. 根据证书申请请求签发证书
假设收到一个证书请求文件名为req.pem,文件格式应该是PKCS#10格式(标准证书请求格式)。
 
首先可以查看一下证书请求的内容,执行命令:
openssl req -in req.pem -text -noout
将看到证书请求的内容,包括请求者唯一的名字(DN)、公钥信息(可能还有一组扩展的可选属性)。
 
执行签发命令:
openssl ca -in req.pem -out cert.pem -config "$HOME/testca/conf/testca.conf"
执行过程中会要求输入访问CA的私钥密码(刚才设置的888888)。
 
完成上一步后,签发好的证书就是cert.pem,另外$HOME/testca/newcerts里也会有一个相同的证书副本(文件名为证书序列号)。
你可以执行以下语句来查看生成的证书的内容:
openssl x509 -in cert.pem -text -noout
 
2. 吊销证书(作废证书)
一般由于用户私钥泄露等情况才需要吊销一个未过期的证书。(当然我们用本测试CA时其时很少用到该命令,除非专门用于测试吊销证书的情况)
假设需要被吊销的证书文件为cert.pem,则执行以下命令吊销证书:
openssl ca -revoke cert.pem -config "$HOME/testca/conf/testca.conf"
 
3. 生成证书吊销列表文件(CRL)
准备公开被吊销的证书列表时,可以生成证书吊销列表(CRL),执行命令如下:
openssl ca -gencrl -out testca.crl -config "$HOME/testca/conf/testca.conf"
还可以添加-crldays和-crlhours参数来说明下一个吊销列表将在多少天后(或多少小时候)发布。
 
可以用以下命令检查testca.crl的内容:
openssl crl -in testca.crl -text -noout
 
三. 自己生成公钥密钥,并用测试CA签发数字证书
我们在平时测试时,可以自己用openssl为服务器或用户生成公钥密钥,并用上面创建的CA签发对应私钥(密钥)的数字证书。
假设,我们就用刚才创建CA的操作系统用户为名为testuser的用户创建数字证书,我们要把待创建的私钥、证书等都放在目录$HOME/testuser下:
 
1. 创建密钥和证书请求(证书请求里包含了公钥)
创建$HOME/testuser目录并执行命令:
mkdir $HOME/testuser
cd $HOME/testuser
openssl req -newkey rsa:1024 -keyout testkey.pem -keyform PEM -out testreq.pem -outform PEM -subj "/O=TestCom/OU=TestOU/CN=testuser"
执行过程中需要输入私钥的保护密码,假设我们输入密码: 222222
 
执行完后,testkey.pem即为用户的密钥,而testreq.pem即为证书请求。
可以用openssl req -in testreq.pem -text -noout查看证书请求的内容。
 
2. 用测试CA为testuser签发证书
同样还在$HOME/testuser目录下执行命令:
openssl ca -in testreq.pem -out testcert.pem -config "$HOME/testca/conf/testca.conf"
执行过程中需要输入CA的密钥保护密码(刚才设置的888888),并且最后询问你是否要给该用户签发证书时要选y。
 
执行完后,testcert.pem即为证书,
可以用命令openssl x509 -in testcert.pem -text -noout查看证书内容。
 
3. 制作一个PKCS12格式的文档(个人数字证书)
我们制作的这个PKCS#12文件将包含密钥、证书和颁发该证书的CA证书。该文件可以直接用于服务器数字证书或个人数字证书。
把前几步生成的密钥和证书制作成一个pkcs12文件的方法执行命令:
openssl pkcs12 -export -in testcert.pem -inkey testkey.pem -out testuser.p12 -name testuser -chain -CAfile "$HOME/testca/cacert.pem"
执行过程中需要输入保护密钥的密码(222222),以及新的保护pkcs12文件的密码。
 
执行完后,testuser.p12即为pkcs12文件。你可以直接拷贝到windows下,作为个人数字证书,双击导入IE后就可以使用了。该文件也可以直接用于tomcat作为服务器证书使用(我尽量在近期再写一篇关于如何自己用tomcat建立HTTPS双向认证测试环境的文章)。
 
若要查看testuser.p12的内容可以用命令openssl pkcs12 -in testuser.p12
 
说明:
    – 本文中名词“个人数字证书”意识为包含私钥和证书的实体,而不是单指只保护公钥的数字证书。
    -  openssl版本OpenSSL 0.9.8g 19

########################
# OpenSSL 配置文件示范 #
########################
# [注意]这个示范文件并不是默认设置。
HOME = . # 默认的随机数种子文件,建议设置为 /dev/random 或 /dev/urandom
RANDFILE $ENV::HOME/.rnd # 扩展对象定义
# 比如,OpenSSL中并未定义X.509证书的扩展项,在使用到的时候就会从下面对扩展对象的定义中获取。
# 定义的方法有两种,第一种(反对使用)是存储在外部文件中,也就是这里"oid_file"变量定义的文件。
#oid_file = $ENV::HOME/.oid
# 第二种是存储在配置文件的一个字段中,也就是这里"oid_section"变量值所指定的字段。
oid_section new_oids new_oids ]
# 可以在这里添加扩展对象的定义,例如可以被’ca’和’req’使用。
# 格式如下:
# 对象简称 = 对象数字ID
req ] # 生成的证书中RSA密钥对的默认长度,取值是2的整数次方。建议使用4096以上。
default_bits 1024 # 保存生成的私钥文件的默认文件名
default_keyfile privkey.pem # 生成的私钥文件是否采用口令加密保护,可以设为yes或no。
encrypt_key yes # 读取输入私钥文件时的口令,如果未设置那么将会提示输入。
# input_password = secret
# 保存输出私钥文件时的口令,如果未设置那么将会提示输入。
# output_password = secret
default_md md5 # 为一些字段设置默认的字符串类型,比如证书请求中的城市和组织名称。可能的取值和解释如下:
# default: 包含了 PrintableString, T61String, BMPString 三种类型
# pkix  : 包含了 PrintableString, BMPString 两种类型
# utf8only: 只使用 UTF8 字符串。推荐使用这个,这样可以完美的包含任意字符。
# nombstr : 包含了 PrintableString, T61String 两种类型(不使用 BMPStrings 或 UTF8String 两种多字节字符类型)
string_mask nombstr # 如果设为yes,那么不管是命令行还是配置文件中的字符串都将按照UTF-8编码看待。默认值no表示仅使用ASCII编码。
utf8 no # 如果设为no,那么 req 指令将直接从配置文件中读取证书字段的信息,而不提示用户输入。
prompt yes # 定义输入用户信息选项的"特征名称"字段名,该扩展字段定义了多项用户信息。
distinguished_name req_distinguished_name # 定义证书请求属性的字段名,该扩展字段定义了证书请求的一些属性,但openssl的证书签发工具并不使用它们。
attributes req_attributes # 证书请求扩展的字段名,该扩展字段定义了要加入到证书请求中的一系列扩展项。
# req_extensions = v3_req
x509_extensions v3_ca ##### "特征名称"字段包含了用户的标识信息 #####
req_distinguished_name ]
countryName CN  #只能使用2字母的国家代码
stateOrProvinceName BeiJin
organizationName 
组织名
commonName 
公司名 ##### 证书请求属性字段定义了证书请求的一些属性(都不是必须的) #####
req_attributes ] ##### 要加入到证书请求中的一系列扩展项 #####
v3_req ]
basicConstraints CA:FALSE
keyUsage 
nonRepudiationdigitalSignaturekeyEncipherment #### 生成自签名证书时使用的证书扩展项 #####
### 因为这部分是非必须的,所以不翻译了,事实上你完全可以删除这部分内容
v3_ca ]
# PKIX recommendation.
subjectKeyIdentifier hash
authorityKeyIdentifier 
keyid:always,issuer:always # This is what PKIX recommends but some broken software chokes on critical
# extensions.
#basicConstraints = critical,CA:true
# So we do this instead.
basicConstraints CA:true # Key usage: this is typical for a CA certificate. However since it will
# prevent it being used as an test self-signed certificate it is best
# left out by default.
# keyUsage = cRLSign, keyCertSign
ca ]
default_ca CA_default ##### 默认的CA主配置字段 #####
CA_default ] # 保存所有信息的文件夹,这个变量只是为了给后面的变量使用
dir = ./demoCA # 存放CA本身根证书的文件名
certificate $dir/cacert.pem # 存放CA自身私钥的文件名
private_key $dir/private/cakey.pem # 签发证书时使用的序列号文本文件,里面必须包含下一个可用的16进制数字。
serial $dir/serial # 存放新签发证书的默认目录,证书名就是该证书的系列号,后缀是.pem
new_certs_dir $dir/newcerts # 已生成的证书的默认保存目录
certs $dir/certs # 保存已签发证书的文本数据库文件,初始时为空。
database $dir/index.txt # 存放当前CRL编号的文件,对于v1版本的CRL则必须注释掉该行
crlnumber $dir/crlnumber # 当前CRL文件
crl $dir/crl.pem # 生成的证书撤销列表(CRL)的默认保存目录
crl_dir $dir/crl # 同一个subject是否只能创建一个证书,设为no表示可以创建多个
unique_subject yes # 签发新证书以及CRL时默认的摘要算法,可以使用:md5,md2,mdc2,sha1
default_md sha1 # 通常,证书签发的特种名称(DN)域的各个参数顺序与证书策略的参数顺序一致。
# 但是,如果这里设为yes则保持与证书请求中的参数顺序一致。
preserve no # 当用户需要确认签发证书时显示可读证书DN域的方式。可用值与 x509 指令的 -nameopt 选项相同。
name_opt ca_default
# 当用户需要确认签发证书时显示证书域的方式。
# 可用值与 x509 指令的 -certopt 选项相同,不过 no_signame 和 no_sigdump 总被默认设置。
cert_opt  ca_default # 新证书默认的生效日期,如果未设置则使用签发时的时间,格式为:YYMMDDHHNNSSZ(年月日时分秒Z)
# default_startdate = 080303223344Z
# 新证书默认的失效日期,格式为:YYMMDDHHNNSSZ(年月日时分秒Z)
# default_enddate = 090303223344Z
# 新签发的证书默认有效期,以天为单位
default_days 365 # 从当前CRL(证书撤销列表)到下次CRL发布的间隔天数
default_crl_days 30 # 是否将证书请求中的扩展项信息加入到证书扩展项中去。取值范围以及解释:
# none: 忽略所有证书请求中的扩展项
# copy: 将证书扩展项中没有的项目复制到证书中
# copyall: 将所有证书请求中的扩展项都复制过去,并且覆盖证书扩展项中原来已经存在的值。
copy_extensions none # 定义用于证书请求DN域匹配策略的字段,用于决定CA要求和处理证书请求提供的DN域的各个参数值的规则。
policy  policy_match # 定义X.509证书扩展项的字段。如果没有提供这个字段则生成X.509v1而不是v3格式的证书。
x509_extensions usr_cert # 定义生成CRL时需要加入的扩展项字段。如果没有定义则生成v1而不是v2版本的CRL。
# crl_extensions = crl_ext
policy_match ]
countryName  match
stateOrProvinceName 
match
organizationName 
match
commonName  
supplied
organizationalUnitName 
optional
emailAddress  
optional ### 下面的部分由于都是非必须的部分,因此也不翻译了。
### 事实上你完全可以从配置文件中删除这些内容
usr_cert ] # These extensions are added when ’ca’ signs a request. basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
nsComment   "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier
=keyid,issuer # This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren’t
# deprecated according to PKIX.
# subjectAltName=email:move
crl_ext ] # CRL extensions.
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
authorityKeyIdentifier=keyid:always,issuer:always proxy_cert_ext ]
# These extensions should be added when creating a proxy certificate basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
nsComment   "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier
=keyid,issuer:always # This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren’t
# deprecated according to PKIX.
# subjectAltName=email:move
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo 

########
# 语法 #
########
#
# 变量 = 值
#
# 语法很简单,一看就懂,但是有几点需要说明:
# 1. 字符串值最好使用双引号界定,并且其中可以使用"\n","\r","\t"这些转义序列("\"怎么表示?)。
# 2. 可以使用 ${变量名} 的形式引用同一字段中的变量,使用 ${字段名::变量名} 的形式引用其它字段中的变量。
# 3. 可以使用 ${EVP::环境变量} 的形式引用操作系统中定义的环境变量,若变量不存在则会导致错误。
# 4. 可以在默认字段定义与操作系统环境变量同名的变量作为默认值来避免环境变量不存在导致的错误。
# 5. 如果在同一字段内有多个相同名称的变量,那么后面的值将覆盖前面的值。
#

############
# 默认字段 #
############
# 此部分是默认字段[配置段],必须放在所有字段之前。
# 读取配置文件数据时,会首先根据字段名称去寻找相应的配置段,如果没有找到则会使用这里的默认字段。

# 定义 HOME 的默认值,防止操作系统中不存在 HOME 环境变量。
 

 

 

 

############################################################################################################
####################
##  证书请求配置  ##
####################
# 在申请证书之前通常需要首先生成符合 PKCS#10 标准的证书请求封装格式。
# openssl 的 req 指令实现了产生证书请求的功能,其相关选项的默认值就来自于这里的设置。
# 证书请求的配置分成几个字段,包括一个基本字段和几个附属字段。

##### 证书请求配置的"基本字段",其它附属字段都以它为入口 #####
 

 

 

 

 

# 签名默认使用的信息摘要算法,可以使用:md5,sha1,mdc2,md2
 

 

 

 

 

 

 

# 生成自签名证书时要使用的证书扩展项字段名,该扩展字段定义了要加入到证书中的一系列扩展项。
 

 

 

 

 

 

 

# Some might want this also
# nsCertType = sslCA, emailCA

# Include email address in subject alt name: another PKIX recommendation
# subjectAltName = email:copy
# Copy issuer details
# issuerAltName = issuer:copy

# DER hex encoding of an extension: beware experts only!
# obj=DER:02:03
# Where ’obj’ is a standard or added object
# You can even override a supported extension:
# basicConstraints = critical, DER:30:03:01:01:FF

############################################################################################################
####################
##  证书签发配置  ##
####################
# openssl 的 ca 指令实现了证书签发的功能,其相关选项的默认值就来自于这里的设置。

# 这个字段只是通过唯一的default_ca变量来指定默认的CA主配置字段的入口(-name 命令行选项的默认值)
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

##### 证书请求信息的匹配策略 #####
# 变量名称是DN域对象的名称,变量值可以是:
# match: 该变量在证书请求中的值必须与CA证书相应的变量值完全相同,否则拒签。
# supplied: 该变量在证书请求中必须提供(值可以不同),否则拒签。
# optional: 该变量在证书请求中可以存在也可以不存在(相当于没有要求)。
# 除非preserve=yes或者在ca命令中使用了-preserveDN,否则在签发证书时将删除匹配策略中未提及的对象。
 

 

 

# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.

 

 

# This is OK for an SSL server.
# nsCertType   = server

# For an object signing certificate this would be used.
# nsCertType = objsign

# For normal client use this is typical
# nsCertType = client, email

# and for everything including object signing:
# nsCertType = client, email, objsign

# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment

# This will be displayed in Netscape’s comment listbox.
 

 

 

# Copy subject details
# issuerAltName=issuer:copy

#nsCaRevocationUrl  = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName

 

 

# issuerAltName=issuer:copy
 

 

# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.

 

 

# This is OK for an SSL server.
# nsCertType   = server

# For an object signing certificate this would be used.
# nsCertType = objsign

# For normal client use this is typical
# nsCertType = client, email

# and for everything including object signing:
# nsCertType = client, email, objsign

# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment

# This will be displayed in Netscape’s comment listbox.
 

 

 

# Copy subject details
# issuerAltName=issuer:copy

#nsCaRevocationUrl  = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName

# This really needs to be in place for it to be a proxy certificate.