1 概述
本文之所以称之为半自动化,是因为证书的申请并非日常工作,只是一段时间才需要申请,同时,在创建证书和办法证书的时候,有些参数需要根据用户的需求自己调整,如证书的有效时间,还有,是否给私钥加密等等,因为叫脚本设置为半自动化,手动输入一些参数,到达用户的需求。当然如果环境是固定,参数也是固定,有效时间固定,该脚本配合crontab也可以实现自动化申请和颁发等操作
CA中心又称CA机构,即证书授权中心(Certificate Authority ),或称证书授权机构。本文将介绍通过openssl这个工具如何创建根CA,子CA,证书申请,证书颁发 和吊销证书等操作进行介绍
2 概念
2.1创建私有CA
openssl的配置文件:/etc/pki/tls/openssl.cnf.这个配置文件里的相关配置,如证书信息匹配策略,证书的相关文件保存路径和命名都在这里做规定,本文将在文末附属该配置文件部分重要参数的介绍
其中,证书信息匹配策略有三种:匹配、支持和可选
匹配指要求申请填写的信息跟CA设置信息必须一致,支持指必须填写这项申请信息,可选指可有可无
用户在自己机器生成证书请求文件后,将对应请求文件发给服务器,服务器收到请求文件后,确认无误,将对于进行签发,并把证书发给申请主机
2.2 证书申请步骤
证书的申请有如下四个步骤
a. 生成申请请求
由客户端自己创建,完成后将生成的文件发送到证书签发机构
b. RA核验,
RA(Registration Authority),数字证书注册审批机构。RA系统是CA的证书发放、管理的延伸。它负责证书申请者的信息录入、审核以及证书发放等工作(安全审计)。同时,对发放的证书完成相应的管理功能(安全管理)。
c. CA签署
核验通过后,在CA服务器生成证书
d. 获取证书
将创建完成的证书发给申请者
3. 实验步骤
3.1 创建根CA
3.1.1、创建所需要的文件
touch /etc/pki/CA/index.txt
#生成证书索引数据库文件
echo 01 >/etc/pki/CA/serial
#指定第一个颁发证书的序列号
3.1.2 CA自签证书
要先有私钥,才能给自己颁发证书
生成私钥,文件名是固定的,而且目录也是当前固定的目录,因为在配置文件里指定了路径和名称
cd /etc/pki/CA/
执行以下这步后会在该目录下生成加密的文件
(umask 066; openssl genrsa -out /etc/pki/CA/private/cakey.pem -des 3 2048)
.生成自签名证书,给自己签名,-x509是关键字,表示要自己申请,而且给自己颁发证书
这里文件的路径都是有要求的,以下要输入密码
openssl req -new -x509 -key /etc/pki/CA/private/cakey.pem -days 7300 -out /etc/pki/CA/cacert.pem
要输入密码,然后要提交信息
-new: 生成新证书签署请求
-x509: 专用于CA生成自签证书
-key: 生成请求时用到的私钥文件
-days n:证书的有效期限
-out /PATH/TO/SOMECERTFILE: 证书的保存路径
3.2 创建子CA
3.2.1子CA上创建私钥和证书请求文件
方法和根CA基本一致
在 /etc/pki/CA下创建serial 和index.txt
以下加密des3,如果加密,每次办法都要输入密码
作为子CA,不能给自己颁发证书,要向根CA申请证书,不加x509选项
生成subca.csr文件,把这个文件复制到服务器根CA对应的目录/etc/kpi/CA/下
3.2.2根CA上颁发证书给子CA
这里对根私钥加了口令,所有要先输入密码,才能输入信息
生成文件subca.crt,把这个文件拷贝到子CA上,注意,这个文件拷贝到子CA上的时候,要把名称改成
cacert.pem,才能作为服务器端的私钥给客户端来签名颁发
到这里服务器端根CA操作完成,此时子CA可以给其他的客户端颁发证书了
3.2.3验证
找一台机器,向子CA申请证书
生成请求文件,这里输入的选项要和根CA的配置文件策略一致
完成后,将这个请求文件发送到子CA上
这个操作在3.3里讲到
3.2.4子CA颁发证书给客户端
建议颁发的证书文件都放在统一的目录下,颁发的第一个证书是在serial从01开始
openssl ca -in rhel5.csr -out certs/rhel5.crt -days 300
出现如下报错,有的奇怪,都是henan,但是还是报错,因为这客户端版本5上的openssl版本和子CA上版本不一致,
导致不兼容。解决颁发是升级openssl的版本为同一版本
3.3颁发证书
3.3.1 在需要使用证书的主机生成证书请求
给web服务器生成私钥,test.key这个名字可以自命名,但是后缀不能该,2048是长度
(umask066; opensslgenrsa-out /etc/pki/tls/private/test.key2048)
生成证书申请文件,用私钥来生成请求文件,后缀一般建议为csr,和服务器生成基本一致,少了关键字-x509,表示不是自签名,而是申请证书
-days 365申请时间,这个时间是没有意义的,因为是由服务器端颁发的时候指定,客户端指定时间是没有意义的
openssl req -new -key /etc/pki/tls/private/test.key -days 365 -out /etc/pki/tls/test.csr
这里的请求文件名(key名)每一次是默认要不一样的,如果要运行,需要更改index.txt.attr里的yes改成no
这里私钥文件没有加密des,所以和服务器不一样,不需要回车后填密码,只需要回车后填入信息
国家,省,公司这三项是配置模板里规定match的,所以要和服务器端一样
3.3.2 发送请求文件给CA
将证书请求文件传输给CA,可以用scp拷贝到服务器端对应的路径
3.3.3CA签署证书,并将证书颁发给请求者
-days 365这里指定的时间是有效期,让客户端用多久的时间,有效的,不指定默认就是服务配置文件设定的时间
commonName = supplied这个字段是supplied,如果新的证书请求的信息的commonName和之前的请求信息一样,,那么当上一个请求信息生成的证书还没有被吊销的时候,新的证书将不再生成,生成0字节的文件
openssl ca -in /tmp/test.csr -out /etc/pki/CA/certs/test.crt -days 365
完成后把证书文件发回给客户端
3.4吊销证书
当客户出现异常,服务器端可以吊销证书
3.4.1在客户端获取要吊销的证书的serial
openssl x509 -in /PATH/FROM/CERT_FILE [-noout|-serial|-subject]
3.4.2在CA上吊销
根据客户提交的serial与subject信息,对比检验是
否与index.txt文件中的信息一致,吊销证书:
这一步是在CA服务上操作的,吊销后证书状态为R
openssl ca -revoke /etc/pki/CA/newcerts/SERIAL.pem
吊销后,要把这个信息发布出去。客户端才知道哪些证书被吊销
3.4.3指定第一个吊销证书的编号
在服务器根CA上操作
crlnumber证书吊销列表编号,在index.txt这个文件里可以看到证书的状态
echo 01 > /etc/pki/CA/crlnumber
/etc/pki/CA/crlnumber里保存的数字表示
接下去要吊销的是第几个证书,如已经吊销了5个证书,则当前文件保存的就是06,表示接下去要吊销第6个证书
注意:第一次更新证书吊销列表前,才需要执行,如果之前已经吊销过证书,这个语句就不需要执行了
3.4.4更新证书吊销列表
把这个文件发布到官方网站上,让所有使用者都知道证书被吊销了
openssl ca -gencrl -out /etc/pki/CA/crl/crl.pem
3.4.5查看crl文件
执行如下语句进行查看,不是必须的步骤
openssl crl -in /etc/pki/CA/crl/crl.pem -noout -text
4 半自动化脚本
以下的脚本,将实现以上5个步骤,为了方便演示,将操作写在同一脚本里。通过选项操作对应的操作。
用户只需要输入相关操作,就可以完成CA的搭建和证书申请,颁发,吊销等操作
脚本如下
#!/bin/bash
#
#******************************************************************************
#Author: Sunny
#Date: 2017-09-09
#FileName: install_ca.sh
#version: 1.0
#Your change info:
#Description: For auto create CA,subCA,generate and revoke cert
#Copyright(C): 2017 All rihts reserved
#*****************************************************************************
CApath=/etc/pki/CA
certpath=/etc/pki/CA/certs
tlspath=/etc/pki/tls
tlsprivatepath=/etc/pki/tls/private
caprivatepate=/etc/pki/CA/private
#check certification info,run below cmd
#openssl x509 -in cent7bbaa.crt -noout -text
#check certification request infomation,run cmd as below
#openssl req -noout -text -in aa.csr
#check certification status,run cmd as below,01 is serial number
#openssl ca -status 01
active_ip(){
actip=$(ip a| grep -E "[0-9]+\/"| cut -d / -f1|sed -nr 's@.*( [0-9]+\.[^0][0-9]*\.[0-9]+\.[0-9]+)@\1@p'|cut -d " " -f2|head -1)
}
pri_key_name(){
echo "private key name should end with .key,such as clent.key"
read -p "enter your private key name(default:client."$actip".key) " prikey
if [ -z ${prikey:-} ];then
prikey=client."$actip".key
fi
}
cli_csr_name(){
echo "Cert request name should end with .crs,such as clent.csr"
read -p "enter your cert request name(default:client."$actip".csr) " clicsr
if [ -z ${clicsr:-} ];then
clicsr=client."$actip".csr
fi
}
key_length(){
echo "key length should be one of 1024,2048,4096"
read -p "enter your private key length(default:2048): " length
if [ -z ${length:-} ]; then
length=2048
fi
}
root_pre(){
[ -e "$CApath"/index.txt ] || touch "$CApath"/index.txt
[ -e "$CApath"/serial ] || echo 01 > "$CApath"/serial
#create a server private key
echo "unless you have modify ca private key name in /etc/pki/tls/openssl.cnf,only use default cakey.pem"read -p "enter your ca private key name accord to openssl.cnf (default:cakey.pem) " cakey
if [ -z ${cakey:-} ];then
cakey=cakey.pem
fi
key_length
if [ -e "$caprivatepate"/"$cakey" ] ;then
echo "The server already have private key,$cakey, under "$caprivatepate"/,please check"
else
umask 066;
read -p "enter yes to encrypt private key,other enter will no enrypt: " yorn
if [ "$yorn" = yes ];then
read -p "enter encrypt key word(eg:-des3): " encry
openssl genrsa -out "$caprivatepate"/"$cakey" "$encry" "$length"
else
openssl genrsa -out "$caprivatepate"/"$cakey" "$length"
fi
umask 022;
fi
}
echo "Enter 1 : run at root CA server and rootca_sig_itself"
echo "Enter 2 : run at sub_server,generate a private key and signature request file,and send request file to root CA "
echo "Enter 3 : run at client host,generate certification requst file and send to server auto"
echo "Enter 4 : run at server,generate certification and send to client auto"
echo "Enter 5 : run at server,to revoke some certification"
read -p "Please input your choice: " choice
case $choice in
1)
root_pre;
#generate a signature certificate for itself,-x509 is key work,means it signature to itself
echo "unless you have modify ca signature certificate name in /etc/pki/tls/openssl.cnf,only use default cacert.pem"
read -p "enter your ca signature cer name accord to openssl.cnf (default:cacert.pem) " cacert
if [ -z ${cacert:-} ];then
cacert=cacert.pem
fi
if [ -e "$CApath"/"$cacert" ] ;then
echo "The server already have signature certificate,"$cacert" under "$CApath",please check"
else
openssl req -new -x509 -key "$caprivatepate"/"$cakey" -days 7300 -out "$CApath"/"$cacert"
fi
;;
2)
root_pre;
#generate a signature certificate request file and send to root CA
read -p "enter your ca signature cer request file (default:subca.csr): " subcacert
if [ -z ${subcacert:-} ];then
subcacert=subca.csr
fi
if [ -e "$CApath"/"$subcacert" ] ;then
echo "The sub ca already have signature certificate,"$subcacert"under "$CApath",please check"
else
openssl req -new -key "$caprivatepate"/"$cakey" -days 7300 -out "$CApath"/"$subcacert"
fi
#send request file to root CA
read -p "which root CA would you send(default:192.168.32.61): " serip
if [ -z ${serip:-} ];then
serip=192.168.32.61
fi
expect -c "
spawn scp "$CApath"/"$subcacert" root@"$serip":"$CApath"/certs/
expect {
\"*assword\" {set timeout 500; send \"Pass1234\r\"; }
\"yes/no\" { send \"yes\r\"; exp_continue; }
}
expect eof"
;;
3)
echo "Please check whether you are in client"
#client generate private key
active_ip
pri_key_name
cli_csr_name
key_length
if [ -e "$tlsprivatepath"/"$prikey" ] ;then
echo "The client already have private key "$prikey" under "$tlsprivatepat",please check"
else
umask 066;
if [ "$yorn" = yes ];then
read -p "enter encrypt key word(eg:-des3): " encry
openssl genrsa -out "$tlsprivatepath"/"$prikey" "$encry" "$length"
else
openssl genrsa -out "$tlsprivatepath"/"$prikey" "$length"
fi
umask 022;
fi
#generate a signature certificate to root ca,without -x509.
if [ -e "$tlspath"/"$clicsr" ] ;then
echo "The client already have signature certificate request file,"$clicsr" under "$tlspath",please check"
else
openssl req -new -key "$tlsprivatepath"/"$prikey" -out "$tlspath"/"$clicsr"
fi
#send request file to CA
read -p "which CA would you send(default:192.168.32.61): " serip
if [ -z ${serip:-} ];then
serip=192.168.32.61
fi
expect -c "
spawn scp "$tlspath"/"$clicsr" root@"$serip":"$CApath"/certs/
expect {
\"*assword\" {set timeout 500; send \"Pass1234\r\"; }
\"yes/no\" { send \"yes\r\"; exp_continue; }
}
expect eof"
;;
4)
read -p "enter which requst file in "$CApath"/certs you want want server to generate as certification(eg:cent7.csr): " clientrsq
read -p "enter new client cetification name(eg:cent7.crt): " crtname
[ -e "$CApath"/certs/"$crtname" ] && { echo $crtname exist,please check;exit 6; }
read -p "enter many days would the cert be avlid(eg:365): " days
if [ -e "$CApath"/certs/"$clientrsq" ];then
#attention,no only $crtname be generated,but also serial.pem file will be generate under dir newcerts
openssl ca -in "$CApath"/certs/"$clientrsq" -out "$CApath"/certs/"$crtname" -days "$days"
else
echo "$clientrsq does no exist in "$CApath"/certs,please check"
fi
read -p "which client would you send(eg:192.168.32.61): " clip
expect -c "
spawn scp "$CApath"/certs/"$crtname" root@"$clip":"$tlspath"
expect {
\"*assword\" {set timeout 500; send \"Pass1234\r\"; }
\"yes/no\" { send \"yes\r\"; exp_continue; }
}
expect eof"
;;
5)
read -p "Please input the serial number you want to revoke(eg:03): " sernum
echo "you can run cmd openssl x509 -in cent7bt.crt -noout -text to check the serial number,cent7bt.crt is the certification you want to revoke"
openssl ca -revoke /etc/pki/CA/newcerts/"$sernum".pem
crlnum=$(cat /etc/pki/CA/crlnumber)
[ -z $crlnum ] && echo 01 > /etc/pki/CA/crlnumber;
openssl ca -gencrl -out /etc/pki/CA/crl/crl.pem;
#check the the crl list
#openssl crl -in /etc/pki/CA/crl/crl.pem -noout -text
;;
*)
echo "your input is wrong,please check"
;;
esac
unset ip
unset crtname
unset CApath
unset clinetsrq
5 /etc/pki/tls/openssl.cnf 讲解
本附录只针对一些重要字段用中文做了备注,内容如下
/etc/pki/tls/openssl.cnf
CA的配置文件,这个文件文件很关键,配置和CA密切相关
[ CA_default ] 这里新创建的文件名必须一样,如果要改,就要跟defaults一样
dir = /etc/pki/CA # Where everything is kept,CA工作目录,CA信息保存的目录
#
certs = $dir/certs # Where the issued certs are kept,定义一个变量
crl_dir = $dir/crl # Where the issued crl are kept,证书吊销列表
database = $dir/index.txt # database index file.这个是一个文件,保存证书数据库,如颁发了哪些证书,证书编号,证书状态等信息保存在这里,这个文件默认没有,要手工创建一个空文件,颁发证书的时候,会手动颁发,没有空文件的话,颁发的时候会报错
#unique_subject = no # Set to 'no' to allow creation of
# several ctificates with same subject.
new_certs_dir = $dir/newcerts # default place for new certs.默认新证书的放置目录,这个文件是自动生成的,用数字编号
certificate = $dir/cacert.pem # The CA certificate,CA自己的证书,根CA自己给自己颁发,子CA上级颁发
serial = $dir/serial # The current serial number,要手动创建,当前系列号,是一个16进制数,实际意义是下一个证书的编号,默认是从00开始,可以自己指定开始的值,必须是16进制数
crlnumber = $dir/crlnumber # the current crl number,证书吊销编号,也是指下一个证书被吊销的编号,即下一个被吊销的证书的编号
# must be commented out to leave a V1 CRL
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/cakey.pem# The private key。CA的私钥
RANDFILE = $dir/private/.rand # private random number file,随机数
x509_extensions = usr_cert # The extentions to add to the cert
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt = ca_default # Subject Name options,命名方式
cert_opt = ca_default # Certificate field options
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
# so this is commented out by default to leave a V1 CRL.
# crlnumber must also be commented out to leave a V1 CRL.
# crl_extensions = crl_ext
default_days = 365 # how long to certify for,证书的有效期,可以指定
default_crl_days= 30 # how long before next CRL,CRL的有效期
default_md = default # use public key default MD
preserve = no # keep passed DN ordering
# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy = policy_match,策略,由下面来指定,表示客户要申请证书的时候,要求客户提供的信息
# For the CA policy
[ policy_match ] 这个是系统默认的策略
countryName = match 国家,match是必须匹配,两边都是同样的信息,其他的可以不一样
stateOrProvinceName = match 省,match如果不一样,就会拒绝,不会颁发证书
organizationName = match 组织
organizationalUnitName = optional 部门
commonName = supplied 通用名,如网站服务器的域名,一般是严格匹配,也可以写成泛域名,申请费用比较贵
emailAddress = optional ,可选项,不强制要求一样
# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ] 给外部使用,不用match选项,都不要求完全一样
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
default_bits = 2048
default_md = sha1
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca # The extentions to add to the self signed cert
6 总结
由于openssl这个命令很强大,有大量的参数,本文只是列出的参数是生成很颁发等必须用到的几个参数,如果需要有更多其他选项,用户可自行添加