OpenSSL生成CA(根证书)并自签署证书(支持IP地址)

Eave 2024.10.30

X.509证书包含三个文件:key,csr,crt

key是服务器上的私钥文件,用于对发送给客户端数据的加密,以及对从客户端接收到数据的解密

csr是证书签名请求文件,用于提交给证书颁发机构(CA)对证书签名

crt是由证书颁发机构(CA)签名后的证书,或者是开发者自签名的证书,包含证书持有人的信息,持有的公钥,以及签署者的签名等信息

备注:在密码学中,X.509是一个标准,规范了公开秘钥认证、证书吊销列表、授权凭证、凭证路径验证算法等。

一、生成CA根证书

生成CA根证书私钥

openssl genrsa -des3 -out ca.pass.key 2048

去除CA私钥中的密码

openssl rsa -in ca.pass.key -out ca.key

生成CA根证书包括了国家(C=CN)、省/州(ST=BJ)、城市(L=BJ)、组织/公司(O=MyRootCA)、组织单位/部门(OU=MyCA)、通用名称(CN=CA)等信息

openssl req -new -sha256 -x509 -days 3650 -key ca.key -subj "/C=CN/ST=Chonqing/L=Chonqing/O=CA/OU=CA/CN=CA" -addext "basicConstraints=critical,CA:TRUE" -addext "keyUsage=critical,keyCertSign,cRLSign" -out ca.crt

注:critical是x509证书扩展中的一个标记,用于指示客户端必须识别并处理该扩展,否则应拒绝证书;可加在basicConstraintskeyUsageextendedKeyUsage中。

二、生成服务器证书

生成v3.ext文件

cat > $HOST.ext << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=critical,CA:FALSE
keyUsage=digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth,clientAuth
subjectAltName=@alt_names

[alt_names]
DNS.1=${HOST}
DNS.2=*.${HOST}
DNS.3=localhost
IP.1=127.0.0.1
EOF

keyUsage各项值的含义:

数字签名(digitalSignature):用于对数据进行签名,确保数据的完整性和来源的真实性

密钥加密(keyEncipherment):用于加密别的密钥,常见于保护会话密钥或对称密钥

数据加密(dataEncipherment):用于公钥加密原始数据

不可否认(nonRepudiation):用于确保发送方不能否认其发送的数据

签名证书(keyCertSign):只有CA证书

签名CRL(cRLSign):只有CA证书

实际选型组合:

普通SSL证书:digitalSignature+keyEncipherment

代码签名/文档签名:digitalSignature,可选nonRepudiation

extendedKeyUsage各项值的含义:

serverAuth:SSL/TLS服务器认证,HTTPS、TLS服务器

clientAuth:SSL/TLS客户端认证,双向SSL、客户端证书

codeSigning:代码签名,可执行文件、驱动程序签名

emailProtection:邮件安全,S/MIME 加密和签名

timeStamping:时间戳,数字签名时间戳


生成服务器证书

openssl req -newkey rsa:2048 -nodes -keyout $HOST.key -subj "/C=CN/ST=Chonqing/L=Chonqing/O=MyRootServer/OU=MyServer/CN=$HOST" -out $HOST.csr

注:PDF阅读器里看到的签名人,通常就是CN字段;中文CN的写法CN=utf8string:张三

对服务器证书进行签名

openssl x509 -req -sha256 -days 3650 -CA ca.crt -CAkey ca.key -CAcreateserial -extfile $HOST.ext -in $HOST.csr -out $HOST.crt

签名成PDF签名证书

openssl x509 -req -sha256 -days 3650 -CA ca.crt -CAkey ca.key -CAcreateserial -addext "authorityKeyIdentifier=keyid,issuer" -addext "basicConstraints=critical,CA:FALSE" -addext "keyUsage=critical,digitalSignature,nonRepudiation" -addext "extendedKeyUsage=emailProtection,codeSigning" -in $HOST.csr -out $HOST.crt

查看服务器证书信息

openssl x509 -text -noout -in $HOST.crt

将CRT证书转为P12证书(可对PDF文件进行签名)

openssl pkcs12 -export -clcerts -in $HOST.crt -inkey $HOST.key -out $HOST.p12

将P12证书转为CRT证书

openssl pkcs12 -in $HOST.p12 -nokeys -out $HOST.crt # 导出证书
openssl pkcs12 -in $HOST.p12 -nocerts -nodes -out $HOST.key # 导出私钥

三、信任CA证书

CentOS

cp ca.crt /etc/pki/ca-trust/source/anchors/
update-ca-trust

Ubuntu

cp ca.crt /usr/local/share/ca-certificates/
update-ca-certificates

Windows

右键点击文件,选择安装证书,选择本地计算机,指定安装到受信任的根证书颁发机构即可。


附:证书生成脚本

#!/bin/bash
set -e  # 遇到错误立即退出

# 显示帮助信息
show_help() {
    cat << EOF
用法: $0 [选项]

选项:
    --host HOST         域名(必需)
    --days N            证书有效天数(默认3650天,约10年)
    --key-size N        密钥大小(默认2048,可选4096)

环境变量:
    SSL_C               国家代码(默认:CN)
    SSL_ST              省份(默认:Chongqing)
    SSL_L               城市(默认:Chongqing)
    SSL_O               组织名称(默认:MyRootCA)
    SSL_OU              组织单位(默认:MyCA)

示例:
    $0 --host example.com
    $0 --host example.com --days 365
    $0 --host example.com --key-size 4096
EOF
}

# 默认配置
HOST=""
DAYS=3650  # 10年
KEY_SIZE=2048

# 证书信息默认值
C=${SSL_C:-"CN"}
ST=${SSL_ST:-"Chongqing"}
L=${SSL_L:-"Chongqing"}
O=${SSL_O:-"MyRootCA"}
OU=${SSL_OU:-"MyCA"}

# 解析命令行参数
while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--help)
            show_help
            exit 0
            ;;
        --host)
            HOST="$2"
            shift 2
            ;;
        --days)
            DAYS="$2"
            shift 2
            ;;
        --key-size)
            KEY_SIZE="$2"
            shift 2
            ;;
        -*)
            echo "错误: 未知选项 $1"
            show_help
            exit 1
            ;;
        *)
    esac
done

# 检查参数
if [ -z "$HOST" ]; then
    show_help
    exit 1
fi

# 简单的域名格式验证
if ! [[ "$HOST" =~ ^[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]$ ]]; then
    echo "警告: 域名格式可能不正确,继续执行..."
fi


# 验证天数是否为数字
if ! [[ "$DAYS" =~ ^[0-9]+$ ]]; then
    echo "错误: 天数必须是数字"
    exit 1
fi

# 验证密钥大小
if [[ "$KEY_SIZE" != "2048" ]] && [[ "$KEY_SIZE" != "4096" ]]; then
    echo "错误: 密钥大小必须是2048或4096"
    exit 1
fi


echo "========================================="


# 判断是否有根证书,有就不生成根证书了
if [ ! -f "ca.crt" ]; then
    echo ""
    echo "=== 生成CA根证书 ==="

    # 生成CA根证书私钥
    echo "1.生成CA根证书私钥 (带密码保护)"
    openssl genrsa -des3 -out ca.pass.key $KEY_SIZE

    # 去除CA根证书私钥中的密码
    echo "2.去除CA根证书私钥中的密码"
    openssl rsa -in ca.pass.key -out ca.key

    # 设置私钥权限
    chmod 600 ca.key

    # 生成CA根证书
    echo "3.生成CA根证书"
    openssl req -new -sha256 -x509 -days $DAYS -key ca.key -subj "/C=$C/ST=$ST/L=$L/O=$O/OU=$OU/CN=CA" -addext "basicConstraints=critical,CA:TRUE" -addext "keyUsage=critical,keyCertSign,cRLSign" -out ca.crt
fi




# 生成服务器证书
echo ""
echo "=== 生成服务器证书 ==="

# 生成v3.ext文件(支持SAN扩展)
echo "1.生成证书扩展配置文件"
cat > $HOST.ext << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=critical,CA:FALSE
keyUsage=digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth,clientAuth
subjectAltName=@alt_names

[alt_names]
DNS.1=${HOST}
DNS.2=*.${HOST}
DNS.3=localhost
IP.1=127.0.0.1
EOF

# 生成服务器私钥和CSR
echo "2.生成服务器私钥和证书签名请求(CSR)"
openssl req -newkey rsa:$KEY_SIZE -nodes -keyout $HOST.key -subj "/C=$C/ST=$ST/L=$L/O=${O}Server/OU=${OU}Server/CN=$HOST" -out $HOST.csr

# 对服务器证书进行签名
echo "3.使用CA证书签署服务器证书"
openssl x509 -req -sha256 -days $DAYS -CA ca.crt -CAkey ca.key -CAcreateserial -extfile $HOST.ext -in $HOST.csr -out $HOST.crt

# 将CRT证书转为P12证书(可对PDF文件进行签名)
echo "4.将CRT证书转为P12证书"
openssl pkcs12 -export -clcerts -in $HOST.crt -inkey $HOST.key -out $HOST.p12

# 将P12证书转为CRT证书
# echo "5.将P12证书转为CRT证书"
# openssl pkcs12 -in $HOST.p12 -nokeys -out $HOST.crt # 导出证书
# openssl pkcs12 -in $HOST.p12 -nocerts -nodes -out $HOST.key # 导出私钥