Skip to content

生成自签证书

参考文献:

  1. https://www.cnblogs.com/nxzblogs/p/16997922.html

  2. https://blog.csdn.net/a735131232/article/details/80526859

1.1 自签名证书方式

除了公网可用的受信证书,在内网环境,我们需要也使用 TLS 证书保障通信安全,这时我们可能会选择自己生成证书,而不是向权威机构申请证书。 可能的原因如下:

  1. 要向权威机构申请证书,那是要给钱的。而在内网环境下,并无必要使用权威证书。

  2. 内网环境使用的可能是非公网域名(xxx.local/xxx.lan/xxx.srv 等),权威机构不签发这种域名的证书。(因为没有人唯一地拥有这个域名)

自己生成的证书有两种方类型:

  1. 自签名 TLS 证书(我签我自己):可以认为是 TLS 证书和 CA 证书都使用同一个密钥对,使用 TLS 证书对它自己进行签名。

    • 测试发现这种方式得到的证书貌似不包含 SAN 属性!因此不支持多域名。
  2. 由本地 CA 证书签名的 TLS 证书:生成两个独立的密钥,一个用作 CA 证书,一个用作 TLS 证书。使用 CA 证书对 TLS 证书进行签名。

一般来说,直接生成一个泛域名的自签名证书就够了,但是它不方便拓展——客户端对每个自签名证书,都需要单独添加一次信任。 而第二种方法生成的证书就没这个问题。

总的来说,使用第一种自签名证书不方便进行拓展,未来可能会遇到麻烦。因此建议使用第二种方法

1.2 自签名生成步骤

以域名:test.local为例子

配置文件

vi openssl.cnf

插入以下脚本保存

[ req ]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn

[ dn ]
C = CN  # Contountry
ST = <state>
L = <city>
O = <organization>
OU = <organization unit>
CN = test.local  # 域名-支持泛域名

[ alt_names ]
DNS.1 = test.local #扩展域名,可以是别的域名
IP.1 = 1.2.3.4 #扩展IP

[ req_ext ]
subjectAltName = @alt_names

[ v3_ext ]
subjectAltName=@alt_names  # Chrome 要求必须要有 subjectAltName(SAN)
authorityKeyIdentifier=keyid,issuer:always
basicConstraints=CA:FALSE
keyUsage=keyEncipherment,dataEncipherment,digitalSignature
extendedKeyUsage=serverAuth,clientAuth

生成证书

有两种方式生成证书,一种是自签名的证书,一种是CA签名,推荐使用CA签名,导入CA签名到浏览器浏览器可识别证书,自签名浏览器无法识别

生成自签名

#生成 2048 位 的 RSA 密钥
openssl genrsa -out ssl.key 2048
#通过第一步编写的配置文件,生成证书签名请求
openssl req -new -key ssl.key -out ssl.csr -config csr.conf
#生成最终的证书,这里指定证书有效期 10000 天
##使用 server.key 进行自签名。这种方式得到的证书不包含 SAN!不支持多域名!
openssl req -x509 -sha256 -days 3650 -key server.key -in server.csr -out server.crt

使用CA证书签名(推荐)

#生成 2048 位 的 RSA 密钥
openssl genrsa -out ssl.key 2048
#通过第一步编写的配置文件,生成证书签名请求
openssl req -new -key ssl.key -out ssl.csr -config csr.conf
#生成 ca 证书,并且使用 CA 证书、CA 密钥对 `csr` 文件进行签名
# ca 私钥
openssl genrsa -out ca.key 2048
# ca 公钥
openssl req -x509 -new -nodes -key ca.key -subj "/CN=test.local" -days 10000 -out ca.crt
# 签名
openssl x509 -req -in ssl.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ssl.crt -days 10000 -extensions v3_ext -extfile csr.conf

验证证书

openssl verify -CAfile ca.crt ssl.crt

、转换文件

如需将crt文件换成PEM文件,则使用一下命令

openssl x509 -in ca.crt -out ca.pem -outform PEM
openssl x509 -in ssl.crt -out ssl.pem -outform PEM

4.1 通过脚本一键生成

可以使用以下脚本直接生成:

vi create_cert.sh
#!/bin/bash -e

# 函数:显示帮助信息
help() {
    cat <<EOF
 ================================================================
 --ssl-domain: 生成 SSL 证书需要的主域名,如不指定则默认为 test.local,如果是 IP 访问服务,则可忽略;
 --ssl-trusted-ip: 一般 SSL 证书只信任域名的访问请求,有时候需要使用 IP 去访问 server,那么需要给 SSL 证书添加扩展 IP,多个 IP 用逗号隔开;
 --ssl-trusted-domain: 如果想多个域名访问,则添加扩展域名(SSL_TRUSTED_DOMAIN),多个扩展域名用逗号隔开;
 --ssl-size: SSL 加密位数,默认 2048;
 --ssl-cn: 国家代码(2 个字母的代号),默认 CN;
 使用示例:
 ./create_self_signed_cert.sh --ssl-domain=www.test.com --ssl-trusted-ip=1.2.3.4 --ssl-trusted-domain=www.test.com  --ssl-size=2048 --ssl-date=3650
 ================================================================
EOF
}

# 显示帮助信息并退出
case "$1" in
    -h|--help) help; exit;;
esac

# 如果没有参数,显示帮助信息并退出
if [[ -z $1 ]]; then
    help
    exit
fi

CMDOPTS="$*"
for OPTS in $CMDOPTS;
do
    key=$(echo ${OPTS} | awk -F"=" '{print $1}' )
    value=$(echo ${OPTS} | awk -F"=" '{print $2}' )
    case "$key" in
        --ssl-domain) SSL_DOMAIN=$value ;;
        --ssl-trusted-ip) SSL_TRUSTED_IP=$value ;;
        --ssl-trusted-domain) SSL_TRUSTED_DOMAIN=$value ;;
        --ssl-size) SSL_SIZE=$value ;;
        --ssl-date) SSL_DATE=$value ;;
        --ca-date) CA_DATE=$value ;;
        --ssl-cn) CN=$value ;;
    esac
done

# 设置默认值
CA_DATE=${CA_DATE:-3650}
SSL_DOMAIN=${SSL_DOMAIN:-'test.local'}
SSL_DATE=${SSL_DATE:-3650}
SSL_SIZE=${SSL_SIZE:-2048}
CN=${CN:-CN}

# 定义证书文件名
SSL_KEY=ssl.key
SSL_CSR=ssl.csr
SSL_CRT=ssl.crt
SSL_PEM=ssl.pem
CA_KEY=ca.key
CA_CRT=ca.crt
CA_PEM=ca.pem
CA_SRL=ca.srl
SSL_CONFIG=openssl.cnf

SSL_CONFIG_INIT="[ req ]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn

[ dn ]
C = CN  # Contountry
ST = <state>
L = <city>
O = <organization>
OU = <organization unit>"

SSL_CONFIG_EXT="
[ v3_ext ]
subjectAltName=@alt_names  # Chrome 要求必须要有 subjectAltName(SAN)
authorityKeyIdentifier=keyid,issuer:always
basicConstraints=CA:FALSE
keyUsage=keyEncipherment,dataEncipherment,digitalSignature
extendedKeyUsage=serverAuth,clientAuth"

generate_ssl_config() {

# 生成 OpenSSL 配置文件
cat > "$SSL_CONFIG" <<EOM
$SSL_CONFIG_INIT
CN = $SSL_DOMAIN

[ alt_names ]
DNS.1 = $SSL_DOMAIN
EOM

    # 添加扩展域名
    if [[ -n $SSL_TRUSTED_DOMAIN ]]; then
        IFS=',' read -ra dns <<< "$SSL_TRUSTED_DOMAIN"
        for i in "${!dns[@]}"; do
            # 注意这里是i+2,因为1已经被主域名占用
            echo "DNS.$((i+2)) = ${dns[$i]}" >> "$SSL_CONFIG"
        done
    fi

    # 添加扩展ip
    if [[ -n $SSL_TRUSTED_IP ]]; then
        IFS=',' read -ra ip <<< "$SSL_TRUSTED_IP"
        for i in "${!ip[@]}"; do
            echo "IP.$((i+1)) = ${ip[$i]}" >> "$SSL_CONFIG"
        done
    fi
    #添加扩展文件
    {
        echo ""
        echo "[ req_ext ]"
        echo "subjectAltName = @alt_names"
        echo "$SSL_CONFIG_EXT"
    } >> "$SSL_CONFIG"
}

# 函数:生成 SSL 证书相关文件
generate_SSL_CRT() {

    # 生成 CA 私钥
    if [[ ! -e $CA_KEY ]]; then
        openssl genrsa -out "$CA_KEY" "$SSL_SIZE"
    fi

    # 生成 CA 证书
    if [[ ! -e $CA_CRT ]]; then
        openssl req -x509 -new -nodes -key "$CA_KEY" -days "$CA_DATE" -out "$CA_CRT" -subj "/C=$CN/CN=$SSL_DOMAIN"
    fi

    # 生成服务 SSL KEY
    openssl genrsa -out "$SSL_KEY" "$SSL_SIZE"

    # 生成服务 SSL CSR
    openssl req -new -key "$SSL_KEY" -out "$SSL_CSR" -config "$SSL_CONFIG"

    # 生成服务 SSL CERT
    openssl x509 -req -in "$SSL_CSR" -CA "$CA_CRT" -CAkey "$CA_KEY" -CAcreateserial -out "$SSL_CRT" -days "$SSL_DATE" -extensions v3_ext -extfile "$SSL_CONFIG"

    #生成 PEM文件
    openssl x509 -in "$CA_CRT" -out "$CA_PEM" -outform PEM
    openssl x509 -in "$SSL_CRT" -out "$SSL_PEM" -outform PEM

    # 附加 CA 证书到 SSL CERT
    cat "$CA_CRT" >> "$SSL_CRT"
}

# 调用函数生成 SSL 配置文件
generate_ssl_config

# 调用函数生成 SSL 证书
generate_SSL_CRT

# 新建文件夹,移动证书文件
mkdir -p "${PWD}/cert"
mv *.crt *.csr *.key *.pem *.srl "$SSL_CONFIG" "${PWD}/cert/"

命令执行在上面有介绍

# 查看帮助
./create_cert.sh -h
#生成域名,--支持泛域名。有通配符最好使用引号隔开,否则可能会有影响
./create_cert.sh --ssl-domain='*.test.com'
#生成ip
./create_cert.sh --ssl-trusted-ip=192.168.1.30