OpenSSL 自签名证书生成脚本(免交互脚本、Nginx 配置、Chrome信任)
前言
在 Linux 下,测试或局域网环境。使用 OpenSSL 生成自签名证书 、Nginx 配置及 Chrome 浏览器信任。
安装 openssl
yum install -y openssl openssl-devel vim
# 或
apt-get install -y openssl vim
方式一
使用 FiloSottile/mkcert 项目生成证书,这个支持全平台生成。
介绍
mkcert 是一个简单的工具,用于制作本地信任的开发证书。它不需要配置。
简化我们在本地搭建 https 环境的复杂性,无需操作繁杂的 openssl 实现自签证书了,这个小程序就可以帮助我们自签证书,在本机使用还会自动信任 CA,非常方便。
使用来自真实证书颁发机构 (CA) 的证书进行开发可能很危险或不可能(对于 example.test
、localhost
或 之类的主机 127.0.0.1
),但自签名证书会导致信任错误。管理您自己的 CA 是最好的解决方案,但通常涉及神秘的命令、专业知识和手动步骤。
mkcert 在系统根存储中自动创建并安装本地 CA,并生成本地信任的证书。mkcert 不会自动配置服务器以使用证书,但这取决于您。
下载
本实验使用 Windows 10 操作系统进行演示说明。mkcert 也支持其他噶平台的安装与使用,自行下载对应的版本安装即可。
安装配置
调出命令提示符
安装 mkcert
输入命令 ,安装 mkcert,将 CA 证书加入本地可信 CA:
mkcert-v1.4.3-windows-amd64.exe -install
使用此命令,就能帮助我们将 mkcert 使用的根证书加入了本地可信 CA 中,以后由该 CA 签发的证书在本地都是可信的。
安装成功成功。提示创建一个新的本地 CA,本地 CA 现在已安装在系统信任存储中。
测试是否安装成功
C:\>mkcert-v1.4.3-windows-amd64.exe --help
Usage of mkcert:
$ mkcert -install
Install the local CA in the system trust store.
$ mkcert example.org
Generate "example.org.pem" and "example.org-key.pem".
$ mkcert example.com myapp.dev localhost 127.0.0.1 ::1
Generate "example.com+4.pem" and "example.com+4-key.pem".
$ mkcert "*.example.it"
Generate "_wildcard.example.it.pem" and "_wildcard.example.it-key.pem".
$ mkcert -uninstall
Uninstall the local CA (but do not delete it).
Advanced options:
-cert-file FILE, -key-file FILE, -p12-file FILE
Customize the output paths.
-client
Generate a certificate for client authentication.
-ecdsa
Generate a certificate with an ECDSA key.
-pkcs12
Generate a ".p12" PKCS #12 file, also know as a ".pfx" file,
containing certificate and key for legacy applications.
-csr CSR
Generate a certificate based on the supplied CSR. Conflicts with
all other flags and arguments except -install and -cert-file.
-CAROOT
Print the CA certificate and key storage location.
$CAROOT (environment variable)
Set the CA certificate and key storage location. (This allows
maintaining multiple local CAs in parallel.)
$TRUST_STORES (environment variable)
A comma-separated list of trust stores to install the local
root CA into. Options are: "system", "java" and "nss" (includes
Firefox). Autodetected by default.
C:\>
查看 CA 证书存放位置
mkcert-v1.4.3-windows-amd64.exe -CAROOT
按“Windows 键 +R”调出运行框,输入 certmgr.msc
命令。打开证书控制台。
生成自签证书
直接跟多个要签发的域名或 ip 就行了,比如签发一个仅本机访问的证书(可以通过 127.0.0.1
和 localhost
,以及 ipv6 地址 ::1
访问)
需要在局域网内测试 https 应用,这种环境可能不对外,因此也无法使用像 Let's encrypt
这种免费证书的方案给局域网签发一个可信的证书,而且 Let's encrypt
本身也不支持认证 Ip。
证书可信的三个要素:
- 由可信的 CA 机构签发
- 访问的地址跟证书认证地址相符
- 证书在有效期内
如果期望自签证书在局域网内使用,以上三个条件都需要满足。很明显自签证书一定可以满足证书在有效期内,那么需要保证后两条。我们签发的证书必须匹配浏览器的地址栏,比如局域网的 ip 或者域名,此外还需要信任 CA。操作如下。
签发证书,加入局域网 IP 地址。
C:\>mkcert-v1.4.3-windows-amd64.exe localhost 127.0.0.1 ::1 192.168.2.25
Note: the local CA is not installed in the Java trust store.
Run "mkcert -install" for certificates to be trusted automatically ⚠️
Created a new certificate valid for the following names 📜
- "localhost"
- "127.0.0.1"
- "::1"
- "192.168.2.25"
The certificate is at "./localhost+3.pem" and the key at "./localhost+3-key.pem" ✅
It will expire on 13 November 2023 🗓
在 mkcert 软件同目录下,生成了自签证书。如图所示。
通过输出,我们可以看到成功生成了 localhost+3.pem
证书文件和 localhost+3-key.pem
私钥文件,只要在 web server 上使用这两个文件就可以了。
高级设置
可以使用 –help
查看帮助,会发现很多高级用法。
-cert-file FILE 、-key-file FILE 、-p12-file FILE
可以定义输出的证书文件名
-client
可以产生客户端认证证书,用于 SSL 双向认证。之前的文章介绍过使用 openssl 脚本的(Nginx SSL 快速双向认证配置 3),可以对比下
-pkcs12
命令可以产生 PKCS12 格式的证书。java 程序通常不支持 PEM 格式的证书,但是支持 PKCS12 格式的证书。通过这个程序我们可以很方便的产生 PKCS12 格式的证书直接给 Java 程序使用。
# 后面还可以继续空格添加其他域名或IP地址,默认是pem格式
mkcert 127.0.0.1 localhost
# 生成p12格式的正式iis可以用,默认密码为:“changeit”
mkcert -pkcs12 192.168.10.123
# 客户端证书,默认是pem格式
mkcert -client 192.168.10.123
# 生成p12格式客户端证书,win用户可以直接导入,默认密码为:“changeit”
mkcert -pkcs12 -client 192.168.10.123
C:\>mkcert-v1.4.3-windows-amd64.exe -help
Usage of mkcert:
$ mkcert -install
Install the local CA in the system trust store.
$ mkcert example.org
Generate "example.org.pem" and "example.org-key.pem".
$ mkcert example.com myapp.dev localhost 127.0.0.1 ::1
Generate "example.com+4.pem" and "example.com+4-key.pem".
$ mkcert "*.example.it"
Generate "_wildcard.example.it.pem" and "_wildcard.example.it-key.pem".
$ mkcert -uninstall
Uninstall the local CA (but do not delete it).
Advanced options:
-cert-file FILE, -key-file FILE, -p12-file FILE
Customize the output paths.
-client
Generate a certificate for client authentication.
-ecdsa
Generate a certificate with an ECDSA key.
-pkcs12
Generate a ".p12" PKCS #12 file, also know as a ".pfx" file,
containing certificate and key for legacy applications.
-csr CSR
Generate a certificate based on the supplied CSR. Conflicts with
all other flags and arguments except -install and -cert-file.
-CAROOT
Print the CA certificate and key storage location.
$CAROOT (environment variable)
Set the CA certificate and key storage location. (This allows
maintaining multiple local CAs in parallel.)
$TRUST_STORES (environment variable)
A comma-separated list of trust stores to install the local
root CA into. Options are: "system", "java" and "nss" (includes
Firefox). Autodetected by default.
C:\>
方式二
创建脚本文件:
vim create_ssl.sh
chmod +x create_ssl.sh
复制以下脚本内容到 create_ssl.sh :
#! /bin/bash
DOMAIN=pskzs.com
## 证书适用IP
IP=$(ip addr|awk '/^[0-9]+: / {}; /inet.*global/ {print gensub(/(.*)\/(.*)/, "\\1", "g", $2)}'|head -n 1)
DOMAIN_EXT=$IP
DATE=3650
echo 'ip为 '$IP
rm -rf ${DOMAIN} ca.key ca.csr ca.crt
mkdir ${DOMAIN}
# 生成CA根证书
## 准备ca配置文件,得到ca.conf
cat > ${DOMAIN}/ca.conf << EOF
[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = CN
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = BeiJing
localityName = Locality Name (eg, city)
localityName_default = BeiJing
organizationName = Organization Name (eg, company)
organizationName_default = pskzs
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_max = 64
commonName_default = PSKZS CA Center
EOF
## 生成ca秘钥,得到ca.key
openssl genrsa -out ca.key 4096
## 生成ca证书签发请求,得到ca.csr
openssl req -new -subj "/C=CN/ST=BeiJing/L=BeiJing/O=pskzs/CN=PSKZS CA Center" -sha256 -out ca.csr -key ca.key -config ${DOMAIN}/ca.conf
## 生成ca根证书,得到ca.crt
openssl x509 -req -days ${DATE} -in ca.csr -signkey ca.key -out ca.crt
# 生成终端用户证书
## 准备配置文件,得到server.conf
cat > ${DOMAIN}/server.conf << EOF
[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
req_extensions = req_ext
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = CN
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = BeiJing
localityName = Locality Name (eg, city)
localityName_default = BeiJing
organizationName = Organization Name (eg, company)
organizationName_default = pskzs
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_max = 64
EOF
echo commonName_default "=" ${DOMAIN} >> ${DOMAIN}/server.conf
cat >> ${DOMAIN}/server.conf << EOF
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
EOF
echo DNS.1 = ${DOMAIN} >> ${DOMAIN}/server.conf
echo DNS.2 = ${DOMAIN_EXT} >> ${DOMAIN}/server.conf
echo IP = ${IP} >> ${DOMAIN}/server.conf
## 生成秘钥,得到server.key
openssl genrsa -out server.key 2048
## 生成证书签发请求,得到server.csr
openssl req -new -subj "/C=CN/ST=BeiJing/L=BeiJing/O=pskzs/CN=${DOMAIN}" -sha256 -out server.csr -key server.key -config ${DOMAIN}/server.conf
## 用CA证书生成终端用户证书,得到server.crt
openssl x509 -req -days 3650 -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt -extensions req_ext -extfile ${DOMAIN}/server.conf
执行脚本:
# 执行命令:./create_ssl.sh <服务器 ip>
./create_ssl.sh 192.168.0.1
生成文件如下:
[root@localhost ssl]# tree
.
├── ca.crt
├── ca.csr
├── ca.key
├── ca.srl
├── creat.sh
├── pskzs.com
│ ├── ca.conf
│ └── server.conf
├── server.crt
├── server.csr
└── server.key
1 directory, 10 files
方式三
如果系统用上方脚本出现错误时,可以尝试该脚本。(感谢 Jack Liu 大佬的开源,大家可以 Star 下,点此前往 GitHub 项目地址 。)
custom.cnf:
# [严正声明]
# 该脚本仅用于开发人员的本地Dev开发和Test测试环境测试,禁止用于其他用途!
[CNF]
# 通配符域名
DOMAIN_NAME="*.wdft.com"
# 浏览器安全策略更改(截至日期:2021-03-11):
# 1.Chrome 58版本开始的安全改变:普通名称支持被删除。使用SAN标记替代。
# 2.Chrome证书被限制为最多398天。
# 有效的398天(天数范围必须小于或等于398天)
VALID_DAYS=398
# TLS文件生成默认当前路径:
SAN_TLS_PATH="tls-ca"
# Default SUBJECT info: SUBJECT=/C=/ST=/L=/O=/OU=/CN=/emailAddress=
# C =>国家名称(2位首字母简写)
# ST =>状态名
# L =>城市名称
# O =>组织名称
# OU =>组织单位
SUBJECT.C=CN
SUBJECT.ST=Shanghai
SUBJECT.L=Shanghai
SUBJECT.O=Localhost
SUBJECT.OU=IT-DEV
gen-tlsv3-san-ca.sh:
#!/usr/bin/env bash
# Generate self-signed SAN CA Domain Name (by openssl extension v3_req)
# @author Jack Liu ljq@Github
# Description:
# Quick self-signed CA certificates are used for local development testing.
#
# Statement:
# this script tool from the visa book is only used to facilitate developers
# to build development and testing environment, prohibited for other purposes!
#
# Browser security policy changes(As of the date: 2021-03-11):
# 1.Security Changes in Chrome 58: Common Name Support Dropped. Using SAN instead.
# 2.Chrome certificates are limited to a maximum of 398 days.
# script version
CLI_VERSION="1.0.0"
# color sign
GREEN_COLOR="\033[32m"
CYAN_COLOR="\033[36m"
YELLOW_COLOR="\033[43;37m"
RED_COLOR="\033[31m"
GREEN_BG_COLOR="\033[47;42m"
CYAN_BG_COLOR="\033[47;46m"
RES="\033[0m"
# --------------------------- functions -----------------------------------
# etc conf
function etc_cnf(){
item=$1
section="CNF"
cnf_file="./custom.cnf"
if [ ! -f "$cnf_file" ]; then
return 0
fi
cnf_options=`awk -F '=' '/'$section'/{a=1}a==1&&$1~/'${item}'/{print $2;exit}' ${cnf_file}`
cnf_options=${cnf_options//\"/}
echo ${cnf_options}
}
# san.conf file init
function san_cnf_init(){
argv_domain_name=$1
# check san conf
SAN_CNF_FILE="san.cnf"
if [ -f "{SAN_CNF_FILE}" ]; then
return true
fi
# create defautl san.cnf(Warning: EOF Monopolize a line)
cat > san.cnf << EOF
[req]
default_bits = 4096
default_md = sha256
distinguished_name = req_distinguished_name
req_extensions = v3_req
[req_distinguished_name]
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${argv_domain_name}
IP.1 = 127.0.0.1
EOF
}
# nginx vhost tpl
function nginx_vhost_tpl(){
argv_san_tls_absolute_path=$1
argv_host_suffix=$2
cat << EOF
server {
listen 443;
server_name www.${host_suffix};
ssl on;
# File format (.crt|.pem)
# ssl_certificate ${argv_san_tls_absolute_path}/${argv_host_suffix}.pem;
ssl_certificate ${argv_san_tls_absolute_path}/${argv_host_suffix}.crt;
ssl_certificate_key ${argv_san_tls_absolute_path}/${argv_host_suffix}.key;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
keepalive_timeout 100;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
root /home/wwwroot;
}
EOF
}
# --------------------------- task exec -----------------------------------
# wildcard doamin name
DOMAIN_NAME=$(etc_cnf "DOMAIN_NAME")
# The valid max 398 days
VALID_DAYS=$(etc_cnf "VALID_DAYS")
# Serial Numbers by datetime
SET_SERIAL=$(date "+%Y%m%d%H%M%S")
# TLS files generate default current path:
SAN_TLS_PATH=$(etc_cnf "SAN_TLS_PATH")
san_tls_absolute_path=$(pwd)/${SAN_TLS_PATH}
# host_suffix
host_suffix=${DOMAIN_NAME//\*\./}
# Default SUBJECT info
subject_c=$(etc_cnf "SUBJECT.C")
subject_st=$(etc_cnf "SUBJECT.ST")
subject_l=$(etc_cnf "SUBJECT.L")
subject_o=$(etc_cnf "SUBJECT.O")
subject_ou=$(etc_cnf "SUBJECT.OU")
SUBJECT="/C=${subject_c}/ST=${subject_st}/L=${subject_l}/O=${subject_o}/OU=${subject_ou}/CN=${DOMAIN_NAME}/emailAddress=dev-test@${host_suffix}"
# help info
help_info="[usage]: [-h | -help | --help] [-v | -V | --version]"
# Current date
now_date=$(date "+%Y-%m-%d")
# help
case $1 in
"-v"|"-V"|"--version"|"-version")
echo -e "${CYAN_COLOR}TLS v3 SAN ca script version:${CLI_VERSION}.${RES}"
exit
;;
"-h"|"-help"|"--help")
echo -e "${YELLOW_COLOR}${help_info}${RES}"
exit
;;
esac
echo -e "${CYAN_BG_COLOR}------------------- [ Task is starting... ] -----------------------${RES}"
# Check openssl installation information
type openssl >/dev/null 2>&1 || { echo >&2 "OpenSSL it's not installed,Please check for installation."; exit 1; }
# san.cnf init
san_cnf_init "${DOMAIN_NAME}"
if [ ! -d "./${SAN_TLS_PATH}" ]; then
mkdir -p ./${SAN_TLS_PATH}
echo -e "create dir: ./${SAN_TLS_PATH}\n"
fi
# Generate ROOT CA
# Since the issuance of the certificate (no password) :
# 1.Generate the root certificate key
openssl genrsa -out ca.key 4096
# 2.Generate self-signed root certificate
openssl req -new -x509 \
-days ${VALID_DAYS} \
-key ca.key \
-out ca.crt \
-subj "$SUBJECT"
# V3 Certificate issuance
# 1.Generate Certificate Key
openssl genrsa -out server.key 4096
# 2.CSR generation using SHA256 algorithm to avoid browser weak password error
openssl req -new \
-sha256 \
-key server.key \
-out server.csr \
-subj "$SUBJECT" \
-config san.cnf
# 3.Check CSR information
v3_csr_verify=$(openssl req -text -in server.csr | grep "X509v3 Subject Alternative Name")
if [[ "$v3_csr_verify" != "" ]] ; then
echo -e "${GREEN_COLOR}CSR X509v3 is verified.${RES}\n"
else
echo -e "${RED_COLOR}CSR X509v3 is not exsit. Please check that the V3 extension module is enabled.${RES}\n"
exit
fi
# 4.Use the root certificate to sign the certificate as CSR and generate a new certificate server.crt
# Remark: Here, the serial parameter is globally unique.
# Certificates with the same serial value on the same device will conflict
openssl x509 -req \
-days ${VALID_DAYS} \
-in server.csr \
-CA ca.crt \
-CAkey ca.key \
-set_serial ${SET_SERIAL} \
-out server.crt \
-sha256 \
-extfile san.cnf \
-extensions v3_req
# 5.Check to see if the CRT certificate is included
v3_crt_verify=$(openssl x509 -text -in server.crt | grep "X509v3 Subject Alternative Name")
if [[ "$v3_crt_verify" != "" ]] ; then
echo -e "${GREEN_COLOR}CRT X509v3 is verified.${RES}\n"
else
echo -e "${RED_COLOR}CRT X509v3 is not exsit. Please check that the v3 extension module is enabled.${RES}\n"
exit
fi
# Copy & rename & bak & move host_suffix files
cp server.key ${host_suffix}.key
cp server.crt ${host_suffix}.crt
cp ca.crt ${host_suffix}_ca.crt
mv ${host_suffix}.key ${san_tls_absolute_path}
mv ${host_suffix}.crt ${san_tls_absolute_path}
mv ${host_suffix}_ca.crt ${san_tls_absolute_path}
# Move process files to ./dev-tls-process/[date]/
if [ ! -d "./${SAN_TLS_PATH}-process/${now_date}" ]; then
mkdir -p "./${SAN_TLS_PATH}-process/${now_date}"
echo -e "create dir: ./${SAN_TLS_PATH}-process/${now_date}\n"
fi
mv ca.key ./${SAN_TLS_PATH}-process/${now_date}/ca.key
mv ca.crt ./${SAN_TLS_PATH}-process/${now_date}/ca.crt
mv server.key ./${SAN_TLS_PATH}-process/${now_date}/server.key
mv server.crt ./${SAN_TLS_PATH}-process/${now_date}/server.crt
mv server.csr ./${SAN_TLS_PATH}-process/${now_date}/server.csr
# Convert .crt to .pem
openssl x509 \
-in ${san_tls_absolute_path}/${host_suffix}.crt \
-out ${san_tls_absolute_path}/${host_suffix}.pem \
-outform PEM
# Nginx vhost server deploy example:
nginx_tpl_conf=$(nginx_vhost_tpl "${san_tls_absolute_path}" "${host_suffix}")
echo -e "${CYAN_BG_COLOR}----- [ Validity of Certificate info ] ---------------${RES}"
dates=$(openssl x509 -in ./${SAN_TLS_PATH}/${host_suffix}.crt -noout -dates)
dates=${dates//notBefore=/Start Datetime\: }
dates=${dates//notAfter=/Expire Datetime\: }
serial_subj=$(openssl x509 -in ./${SAN_TLS_PATH}/${host_suffix}.crt -noout -serial -subject)
echo -e "${serial_subj}\n"
echo -e "${CYAN_COLOR}${dates}${RES}\n"
echo -e "${GREEN_COLOR}The certificate generation completed !${RES}\n"
# generate nginx vhost example .conf
echo "${nginx_tpl_conf}" > ${san_tls_absolute_path}/vhost_${host_suffix}.conf
echo -e "${CYAN_BG_COLOR}----- [ Deployment instructions (Template: Nginx vhost example) ] ----${RES}\n"
echo -e "${CYAN_COLOR}${nginx_tpl_conf}${RES}\n"
echo -e "${CYAN_BG_COLOR}----- [ Client CA import install file ] ----${RES}"
echo -e "${CYAN_COLOR}[Client CA import install file]: ./${SAN_TLS_PATH}/${host_suffix}_ca.crt${RES}\n"
echo -e "${YELLOW_COLOR}The client imports the CA root certificate and sets to add trust.${RES}\n\n"
echo -e "${CYAN_BG_COLOR}------------------- [ Task is completed ] --------------------------${RES}"
exit
生成后的目录结构:
├── custom.cnf # 脚本自定义配置文件
├── tls-ca # 自签证书生成目录
│ ├── vhost_wdft.com.conf # Nginx vhost demo
│ ├── wdft.com_ca.crt # 客户端根证书(导入或安装,添加信任)
│ ├── wdft.com.crt # 服务器密钥对(.crt)
│ ├── wdft.com.key # 服务器密钥对私钥(.key)
│ └── wdft.com.pem # 服务器密钥对(.pem)
│
├── tls-ca-process # 流程文件,用于备份和诊断
│ └── 2021-03-13
│ ├── ca.crt
│ ├── ca.key
│ ├── server.crt
│ ├── server.csr
│ └── server.key
│
├── gen-tlsv3-san-ca.sh
└── san.cnf # SAN: 此文件首次自动生成
配置 Nginx
将 server.crt 和 server.key 配置到 Nginx ,示例如下:
load_module /usr/lib/nginx/modules/ngx_stream_module.so;
worker_processes auto;
events {
worker_connections 1024;
accept_mutex on;
}
http {
include mime.types;
default_type application/octet-stream;
server {
listen 18080 ssl http2;
# 证书配置
ssl_certificate /home/ssl/server.crt;
ssl_certificate_key /home/ssl/server.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
location / {
root /home/html/;
index index.html;
}
}
}
本地信任
安装 ca.crt 证书到本地电脑,操作如下:
双击 ca.crt 证书,选择安装证书
将证书导入到本地计算机
将证书放到 受信任的根证书颁发机构 目录下
点击完成
测试验证
PC 安装证书完成,需要关闭 Chrome 浏览器,重新打开访问。