Skip to main content

OpenSSL自签名证书

· 8 min read
info

利用openssl颁发本地自签名SSL证书.以下脚本来自 github 略有改动

生成证书一共可以分为三步:

  1. 为当前CA生成根证书, 这个证书将被用来签署所有其他的证书,并且必须被这些服务器的客户所信任。(即安装到可信任证书颁发机构列表中去)
  2. 创建证书
  3. 签发证书

注意: 以下shell脚本在Ubuntu20.04成功执行, 其他平台暂未测试。(但Windows不行)

1. 根证书

new-root-ca.sh

#!/bin/bash
##
## new-root-ca.sh - create the root CA
## Copyright (c) 2000 Yeak Nai Siew, All Rights Reserved.
##

KEYBITS=4096
HASHALGO="sha256"
VALID_DAYS=3650
RANDOM_SRC=/dev/urandom

# Create the master CA key. This should be done once.
if [ ! -f ca.key ]; then
echo "No Root CA key found. Generating one"
openssl genrsa -aes256 -out ca.key -rand ${RANDOM_SRC} $KEYBITS
echo ""
fi

# Self-sign it.
CONFIG="root-ca.conf"
cat >$CONFIG <<EOT
[ req ]
default_bits = $KEYBITS
default_keyfile = ca.key
default_md = $HASHALGO
distinguished_name = req_distinguished_name
x509_extensions = v3_ca
string_mask = nombstr
req_extensions = v3_req
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = CN
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Shanghai
localityName = Locality Name (eg, city)
0.organizationName = Organization Name (eg, company)
0.organizationName_default = Chris Yang Certificate Authority
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Certification Services Division
commonName = Common Name (eg, MD Root CA)
commonName_default = Chris Yang CA
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 40
[ v3_ca ]
basicConstraints = critical,CA:true
subjectKeyIdentifier = hash
[ v3_req ]
nsCertType = objsign,email,server
EOT

echo "Self-sign the root CA..."
openssl req -new -x509 -days ${VALID_DAYS} -config $CONFIG -key ca.key -out ca.crt
echo "complete."
rm -f $CONFIG

执行这段脚本后, 可以按提示输入一些信息, 如: 国家, 城市, 组织, 颁发机构等等, 这些就是对应了证书的一些信息。 接下来还有个输入密码的过程, 这个密码对应后面签发证书时的密码,需要牢记。执行完成后会生成两个文件:

  • ca.crt -- 分发给客户并添加到可信的CA数据库中
  • ca.key -- 私钥文件,保密不可泄漏

根证书有效期为十年,只要根证书不失效,执行以下两个命令重新签发证书即可

2. 创建证书

new-server-cert.sh

#!/bin/bash
##
## new-server-cert.sh - create the server cert
## Copyright (c) 2000 Yeak Nai Siew, All Rights Reserved.
##

KEYBITS=4096
HASHALGO="sha256"

# Create the key. This should be done once per cert.
CN=$1
if [ $# -lt 1 ]; then
echo "Usage: $0 <www.domain.com> [subjectAltName1 [san2 ...]]"
exit 1
fi

# force the CN to become a SAN even if no other SANs; Chrome compatibility
subjectAltNames="$*"

# if private key exists, ask if we want to generate a new key
if [ -f $CN.key ]; then
read -p "a key for this cn is already existing, generate a new one? " ANSWER
if [ "$ANSWER" == "Y" ] || [ "$ANSWER" == "y" ]; then
rm -f $CN.key
fi
fi

if [ ! -f $CN.key ]; then
echo "No $CN.key found. Generating one"
openssl genrsa -out $CN.key $KEYBITS
echo ""
fi

# Fill the necessary certificate data
CONFIG="server-cert.conf"
cat >$CONFIG <<EOT
[ req ]
default_bits = $KEYBITS
default_keyfile = server.key
default_md = $HASHALGO
distinguished_name = req_distinguished_name
string_mask = nombstr
req_extensions = v3_req
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = CN
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Shanghai
localityName = Locality Name (eg, city)
localityName_default = Shanghai
0.organizationName = Organization Name (eg, company)
0.organizationName_default = optional
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = optional
commonName = Common Name (eg, www.domain.com)
commonName_default = $CN
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 40
[ v3_req ]
nsCertType = server
basicConstraints = critical,CA:false
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
EOT

# Handle optional Subject Alternate Names
if [ "$subjectAltNames" != "" ]; then
echo "subjectAltName = @alt_names" >> $CONFIG
echo "[alt_names]" >> $CONFIG
numi=1
numd=1
cn_already_added=0

# CN is added to the SAN list automatically
for san in $CN $subjectAltNames; do
# if CN has already been seen, skip it
if [ "$san" = "$CN" ]; then
if [ $cn_already_added -eq 0 ]; then
cn_already_added=1
else
continue #skip to next SAN
fi
fi

# determine if this looks like an IP or a DNS name
echo $san | egrep '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$' &> /dev/null
if [ $? -eq 0 ]; then
echo "IP.$numi = $san" >> $CONFIG
let numi++
else
echo "DNS.$numd = $san" >> $CONFIG
let numd++
fi
done
fi

echo "Fill in certificate data"
openssl req -new -config $CONFIG -key $CN.key -out $CN.csr

rm -f $CONFIG

echo ""
echo "You may now run ./sign-server-cert.sh to get it signed"
# 创建单个证书
$ ./new-server-cert.sh www.test.org
$ ./new-server-cert.sh www.test.org a.test.org b.test.org
# 创建通配符证书
$ ./new-server-cert.sh '*.test.org'

执行shell后会创建出以下两个文件

  • www.test.org.csr -- CA的证书签署请求 (CSR是携带CA所需信息的文件,以便创建证书(CRT)文件,传递给服务器的客户。)
  • www.test.org.key -- 只存在网络服务器的私人加密密钥(证书文件)

3. 签发证书

sign-server-cert.sh

#!/bin/bash
##
## sign-server-cert.sh - sign using our root CA the server cert
## Copyright (c) 2000 Yeak Nai Siew, All Rights Reserved.
##

HASHALGO="sha256"
VALID_DAYS=365
RANDOM_SRC=/dev/urandom

CN=$1
if [ $# -ne 1 ]; then
echo "Usage: $0 <www.domain.com>"
exit 1
fi
if [ ! -f $CN.csr ]; then
echo "No $CN.csr found. You must create that first."
exit 1
fi
# Check for root CA key
if [ ! -f ca.key -o ! -f ca.crt ]; then
echo "You must have root CA key generated first."
exit 1
fi

# Sign it with our CA key #

# make sure environment exists
if [ ! -d ca.db.certs ]; then
mkdir ca.db.certs
fi
if [ ! -f ca.db.serial ]; then
echo '01' >ca.db.serial
fi
if [ ! -f ca.db.index ]; then
cp /dev/null ca.db.index
fi


# create the CA requirement to sign the cert
cat >ca.config <<EOT
[ ca ]
default_ca = default_CA
[ default_CA ]
dir = .
certs = \$dir
new_certs_dir = \$dir/ca.db.certs
database = \$dir/ca.db.index
serial = \$dir/ca.db.serial
RANDFILE = ${RANDOM_SRC}
certificate = \$dir/ca.crt
private_key = \$dir/ca.key
default_days = ${VALID_DAYS}
default_crl_days = 30
default_md = $HASHALGO
preserve = no
x509_extensions = server_cert
policy = policy_anything
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ server_cert ]
#subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
extendedKeyUsage = serverAuth,clientAuth,msSGC,nsSGC
basicConstraints = critical,CA:false
[req]
default_md = $HASHALGO
req_extensions = v3_req
[ v3_req ]
extendedKeyUsage = serverAuth, clientAuth
EOT

# Test for Subject Alternate Names
subjaltnames="`openssl req -text -noout -in $CN.csr | sed -e 's/^ *//' | grep -A1 'X509v3 Subject Alternative Name:' | tail -1 | sed -e 's/IP Address:/IP:/g'`"
if [ "$subjaltnames" != "" ]; then
echo "Found subject alternate names: $subjaltnames"
echo ""
echo "subjectAltName = $subjaltnames" >> ca.config
fi

# revoke an existing old certificate
if [ -f $CN.crt ]; then
echo "Revoking current certificate: $CN.crt"
openssl ca -revoke $CN.crt -config ca.config
fi

# sign the certificate
echo "CA signing: $CN.csr -> $CN.crt:"
openssl ca -config ca.config -extensions v3_req -out $CN.crt -infiles $CN.csr
echo ""
echo "CA verifying: $CN.crt <-> CA cert"
openssl verify -CAfile ca.crt $CN.crt
echo ""

# cleanup after SSLeay
rm -f ca.config
rm -f ca.db.serial.old
rm -f ca.db.index.old
$ ./sign-server-cert.sh a.test.org

签发证书完成后,会生成 a.test.org.crt。使用以下命令能查看证书有效期

$ cat a.test.org.crt | openssl x509 -noout -enddate

签发证书时, 需要输入创建根证书时设置的密码

4. 使用

签发证书后, 会在当前目录生成 *.test.org.key*.test.org.crt 两个文件, 在nginx中配置后即可使用

ssl_certificate /etc/ssl/certs/a.text.org.crt;
ssl_certificate_key /etc/ssl/private/a.text.org.key;