通过自签证书进行C/S信任访问
通过自签证书进行C/S 信任访问
auther: 3inter.net
2025/12/19 11:42
CA管理器
#!/bin/bash
# ============================================
# CA 管理器脚本
# 版本: 4.2
# 功能:创建根CA、签发客户端证书
# 加密标准:RSA 3072, SHA-512
# ============================================
set -e
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# 目录结构
BASE_DIR="${HOME}/my-ca"
ROOT_CA_DIR="${BASE_DIR}/root-ca"
USERS_DIR="${BASE_DIR}/users"
CERTS_DIR="${BASE_DIR}/certs"
PRIVATE_DIR="${BASE_DIR}/private"
CONFIG_DIR="${BASE_DIR}/config"
TEMPLATES_DIR="${BASE_DIR}/templates"
PASSWORD_FILE="${BASE_DIR}/.ca-passphrase"
DB_FILE="${BASE_DIR}/cert-database.csv"
# 初始化目录结构
init_directories() {
echo -e "${GREEN}初始化目录结构...${NC}"
mkdir -p ${ROOT_CA_DIR}/{certs,private,newcerts,crl}
mkdir -p ${USERS_DIR}
mkdir -p ${CERTS_DIR}
mkdir -p ${PRIVATE_DIR}
mkdir -p ${CONFIG_DIR}
mkdir -p ${TEMPLATES_DIR}
# 创建必要的文件
touch ${ROOT_CA_DIR}/index.txt
echo 1000 > ${ROOT_CA_DIR}/serial
echo 1000 > ${ROOT_CA_DIR}/crlnumber
# 初始化证书数据库
echo "username,email,department,organization,country,state,city,created_date,expiry_date,serial_number,status" > ${DB_FILE}
echo -e "${GREEN}目录结构创建完成!${NC}"
echo -e "${YELLOW}用户证书将存放在: ${USERS_DIR}/${NC}"
}
# 密码强度检查函数
check_password_strength() {
local password="$1"
if [ ${#password} -lt 8 ]; then
echo "密码长度至少8位"
return 1
fi
if ! [[ "$password" =~ [A-Z] ]]; then
echo "密码必须包含至少一个大写字母"
return 1
fi
if ! [[ "$password" =~ [a-z] ]]; then
echo "密码必须包含至少一个小写字母"
return 1
fi
if ! [[ "$password" =~ [0-9] ]]; then
echo "密码必须包含至少一个数字"
return 1
fi
if ! [[ "$password" =~ [[:punct:]] ]]; then
echo "密码必须包含至少一个特殊字符"
return 1
fi
echo "密码强度: 强"
return 0
}
# 设置CA密码
set_ca_password() {
echo -e "${YELLOW}设置CA私钥密码${NC}"
echo -e "${CYAN}密码要求:${NC}"
echo " - 长度至少8位"
echo " - 包含大写字母"
echo " - 包含小写字母"
echo " - 包含数字"
echo " - 包含特殊字符"
echo ""
while true; do
read -sp "输入密码: " PASSWORD1
echo
if check_password_strength "$PASSWORD1"; then
read -sp "确认密码: " PASSWORD2
echo
if [ "$PASSWORD1" == "$PASSWORD2" ]; then
echo "$PASSWORD1" > ${PASSWORD_FILE}
chmod 600 ${PASSWORD_FILE}
echo -e "${GREEN}密码设置成功!${NC}"
return 0
else
echo -e "${RED}两次输入的密码不一致!${NC}"
fi
fi
done
}
# 获取CA密码
get_ca_password() {
if [ -f "${PASSWORD_FILE}" ]; then
cat ${PASSWORD_FILE}
else
echo ""
fi
}
# 生成配置文件
generate_config_files() {
echo -e "${GREEN}生成配置文件...${NC}"
# 主配置文件
cat > ${CONFIG_DIR}/ca-config.cnf << 'EOF'
# OpenSSL CA配置文件
[ ca ]
default_ca = CA_default
[ CA_default ]
# 目录和文件设置
dir = $ENV::CA_DIR
certs = $dir/certs
crl_dir = $dir/crl
new_certs_dir = $dir/newcerts
database = $dir/index.txt
serial = $dir/serial
RANDFILE = $dir/private/.rand
# CA证书和私钥
certificate = $dir/certs/ca.crt
private_key = $dir/private/ca.key
# 默认设置
default_days = 365
default_crl_days = 30
default_md = sha512
preserve = no
policy = policy_anything
# 证书策略
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
# 证书请求配置
[ req ]
default_bits = 3072
default_md = sha512
distinguished_name = req_distinguished_name
x509_extensions = v3_ca
[ 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 = Beijing
localityName = Locality Name (eg, city)
localityName_default = Beijing
0.organizationName = Organization Name (eg, company)
0.organizationName_default = My Company
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = IT Department
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 64
# 扩展配置
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
basicConstraints = CA:true
keyUsage = digitalSignature, cRLSign, keyCertSign
[ v3_client_cert ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
subjectAltName = email:copy
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
EOF
echo -e "${GREEN}配置文件生成完成!${NC}"
}
# 创建根CA
create_root_ca() {
echo -e "${GREEN}创建根CA...${NC}"
if [ -f "${ROOT_CA_DIR}/private/ca.key" ]; then
echo -e "${YELLOW}根CA已经存在,是否重新创建? (y/N)${NC}"
read -r response
if [[ ! "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
return
fi
fi
echo -e "${CYAN}请输入根CA的详细信息:${NC}"
read -p "国家代码 (2字母,如CN) [CN]: " COUNTRY
read -p "省/州 [Beijing]: " STATE
read -p "城市 [Beijing]: " LOCALITY
read -p "组织名称 [My Company]: " ORGANIZATION
read -p "组织单元 [Certificate Authority]: " ORGANIZATIONAL_UNIT
read -p "通用名称 (CA名称) [My Root CA]: " COMMON_NAME
read -p "邮箱地址 [ca-admin@example.com]: " EMAIL
COUNTRY=${COUNTRY:-CN}
STATE=${STATE:-Beijing}
LOCALITY=${LOCALITY:-Beijing}
ORGANIZATION=${ORGANIZATION:-"My Company"}
ORGANIZATIONAL_UNIT=${ORGANIZATIONAL_UNIT:-"Certificate Authority"}
COMMON_NAME=${COMMON_NAME:-"My Root CA"}
EMAIL=${EMAIL:-"ca-admin@example.com"}
# 询问是否设置密码
echo -e "${YELLOW}为CA私钥设置密码? (Y/n)${NC}"
read -r USE_PASSWORD
USE_PASSWORD=${USE_PASSWORD:-Y}
if [[ "$USE_PASSWORD" =~ ^([yY][eE][sS]|[yY])$ ]]; then
set_ca_password
CA_PASSWORD=$(get_ca_password)
echo -e "${BLUE}生成带密码的RSA 3072私钥...${NC}"
openssl genrsa -aes256 -passout pass:"${CA_PASSWORD}" \
-out ${ROOT_CA_DIR}/private/ca.key 3072
echo -e "${BLUE}生成自签名根证书...${NC}"
# 创建临时配置文件
cat > /tmp/ca-req.cnf << EOF
[ req ]
default_bits = 3072
default_md = sha512
distinguished_name = dn
prompt = no
x509_extensions = v3_ca
[ dn ]
C = ${COUNTRY}
ST = ${STATE}
L = ${LOCALITY}
O = ${ORGANIZATION}
OU = ${ORGANIZATIONAL_UNIT}
CN = ${COMMON_NAME}
emailAddress = ${EMAIL}
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
basicConstraints = CA:true
keyUsage = digitalSignature, cRLSign, keyCertSign
EOF
openssl req -new -x509 -sha512 -days 7300 \
-config /tmp/ca-req.cnf \
-key ${ROOT_CA_DIR}/private/ca.key \
-passin pass:"${CA_PASSWORD}" \
-out ${ROOT_CA_DIR}/certs/ca.crt
rm -f /tmp/ca-req.cnf
else
echo -e "${YELLOW}生成无密码私钥...${NC}"
openssl genrsa -out ${ROOT_CA_DIR}/private/ca.key 3072
# 创建临时配置文件
cat > /tmp/ca-req.cnf << EOF
[ req ]
default_bits = 3072
default_md = sha512
distinguished_name = dn
prompt = no
x509_extensions = v3_ca
[ dn ]
C = ${COUNTRY}
ST = ${STATE}
L = ${LOCALITY}
O = ${ORGANIZATION}
OU = ${ORGANIZATIONAL_UNIT}
CN = ${COMMON_NAME}
emailAddress = ${EMAIL}
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
basicConstraints = CA:true
keyUsage = digitalSignature, cRLSign, keyCertSign
EOF
openssl req -new -x509 -sha512 -days 7300 \
-config /tmp/ca-req.cnf \
-key ${ROOT_CA_DIR}/private/ca.key \
-out ${ROOT_CA_DIR}/certs/ca.crt
rm -f /tmp/ca-req.cnf
fi
chmod 400 ${ROOT_CA_DIR}/private/ca.key
chmod 444 ${ROOT_CA_DIR}/certs/ca.crt
# 显示证书信息
echo -e "${GREEN}根CA创建完成!${NC}"
echo -e "${CYAN}证书信息:${NC}"
openssl x509 -in ${ROOT_CA_DIR}/certs/ca.crt -noout -subject -issuer -dates
}
# 创建用户证书(每个用户独立文件夹)
create_user_cert() {
echo -e "${GREEN}创建用户证书...${NC}"
if [ ! -f "${ROOT_CA_DIR}/private/ca.key" ]; then
echo -e "${RED}错误:根CA不存在,请先创建根CA!${NC}"
return 1
fi
# 获取用户信息
echo -e "${CYAN}请输入用户信息:${NC}"
while true; do
read -p "用户名 (英文,无空格): " USERNAME
if [[ "$USERNAME" =~ ^[a-zA-Z0-9_\-]+$ ]]; then
break
else
echo -e "${RED}用户名只能包含字母、数字、下划线和连字符!${NC}"
fi
done
read -p "用户全名 (显示名称): " FULL_NAME
read -p "邮箱地址: " EMAIL
read -p "部门: " DEPARTMENT
read -p "组织名称: " ORGANIZATION
read -p "国家代码 [CN]: " COUNTRY
read -p "省/州 [Beijing]: " STATE
read -p "城市 [Beijing]: " LOCALITY
# 设置默认值
FULL_NAME=${FULL_NAME:-$USERNAME}
EMAIL=${EMAIL:-"${USERNAME}@example.com"}
COUNTRY=${COUNTRY:-CN}
ORGANIZATION=${ORGANIZATION:-"My Company"}
DEPARTMENT=${DEPARTMENT:-"IT Department"}
STATE=${STATE:-"Beijing"}
LOCALITY=${LOCALITY:-"Beijing"}
# 检查用户是否已存在
USER_DIR="${USERS_DIR}/${USERNAME}"
if [ -d "${USER_DIR}" ]; then
echo -e "${YELLOW}用户 ${USERNAME} 已存在,是否重新创建? (y/N)${NC}"
read -r response
if [[ ! "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
return
fi
rm -rf "${USER_DIR}"
fi
# 创建用户目录
mkdir -p "${USER_DIR}"
mkdir -p "${USER_DIR}/private"
mkdir -p "${USER_DIR}/certs"
mkdir -p "${USER_DIR}/backup"
# 生成用户私钥
USER_KEY="${USER_DIR}/private/${USERNAME}.key"
echo -e "${BLUE}生成用户私钥...${NC}"
openssl genrsa -out "${USER_KEY}" 3072
chmod 400 "${USER_KEY}"
# 生成证书签名请求
CSR="${USER_DIR}/certs/${USERNAME}.csr"
echo -e "${BLUE}生成证书签名请求...${NC}"
# 创建CSR配置文件
cat > /tmp/user-csr.cnf << EOF
[ req ]
default_bits = 3072
default_md = sha512
distinguished_name = dn
req_extensions = v3_req
prompt = no
[ dn ]
C = ${COUNTRY}
ST = ${STATE}
L = ${LOCALITY}
O = ${ORGANIZATION}
OU = ${DEPARTMENT}
CN = ${FULL_NAME}
emailAddress = ${EMAIL}
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
subjectAltName = email:${EMAIL}
EOF
openssl req -new -sha512 \
-key "${USER_KEY}" \
-out "${CSR}" \
-config /tmp/user-csr.cnf
if [ ! -f "${CSR}" ]; then
echo -e "${RED}生成CSR失败!${NC}"
rm -f /tmp/user-csr.cnf
return 1
fi
echo -e "${GREEN}CSR生成成功${NC}"
# 签署证书
USER_CERT="${USER_DIR}/certs/${USERNAME}.crt"
echo -e "${BLUE}签署用户证书...${NC}"
CA_PASSWORD=$(get_ca_password)
# 创建签名配置文件(包含完整扩展)
cat > /tmp/signing.cnf << EOF
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = ${ROOT_CA_DIR}
certs = \$dir/certs
crl_dir = \$dir/crl
new_certs_dir = \$dir/newcerts
database = \$dir/index.txt
serial = \$dir/serial
RANDFILE = \$dir/private/.rand
certificate = \$dir/certs/ca.crt
private_key = \$dir/private/ca.key
default_days = 365
default_crl_days = 30
default_md = sha512
preserve = no
policy = policy_anything
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ v3_client_cert ]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
subjectAltName = email:copy
EOF
# 使用x509命令直接签署证书(更简单可靠)
if [ -z "$CA_PASSWORD" ]; then
# 无密码情况
openssl x509 -req -sha512 -days 365 \
-in "${CSR}" \
-CA ${ROOT_CA_DIR}/certs/ca.crt \
-CAkey ${ROOT_CA_DIR}/private/ca.key \
-CAcreateserial \
-out "${USER_CERT}" \
-extfile /tmp/user-csr.cnf \
-extensions v3_req
else
# 有密码情况
openssl x509 -req -sha512 -days 365 \
-in "${CSR}" \
-CA ${ROOT_CA_DIR}/certs/ca.crt \
-CAkey ${ROOT_CA_DIR}/private/ca.key \
-passin pass:"${CA_PASSWORD}" \
-CAcreateserial \
-out "${USER_CERT}" \
-extfile /tmp/user-csr.cnf \
-extensions v3_req
fi
if [ ! -f "${USER_CERT}" ]; then
echo -e "${RED}签署证书失败!${NC}"
rm -f /tmp/user-csr.cnf /tmp/signing.cnf
return 1
fi
echo -e "${GREEN}证书签署成功${NC}"
# 创建证书链
CHAIN="${USER_DIR}/certs/${USERNAME}-chain.crt"
cat "${USER_CERT}" > "${CHAIN}"
cat ${ROOT_CA_DIR}/certs/ca.crt >> "${CHAIN}"
# 设置PKCS#12密码
echo -e "${YELLOW}为PKCS#12文件设置密码:${NC}"
while true; do
read -sp "输入PKCS#12密码 (至少8位): " P12_PASSWORD1
echo
if [ ${#P12_PASSWORD1} -lt 8 ]; then
echo -e "${RED}密码至少需要8位!${NC}"
continue
fi
read -sp "确认密码: " P12_PASSWORD2
echo
if [ "$P12_PASSWORD1" == "$P12_PASSWORD2" ]; then
P12_PASSWORD="$P12_PASSWORD1"
break
else
echo -e "${RED}两次输入的密码不一致!${NC}"
fi
done
# 创建PKCS#12文件
P12_FILE="${USER_DIR}/certs/${USERNAME}.p12"
echo -e "${BLUE}创建PKCS#12文件...${NC}"
openssl pkcs12 -export \
-in "${USER_CERT}" \
-inkey "${USER_KEY}" \
-certfile ${ROOT_CA_DIR}/certs/ca.crt \
-out "${P12_FILE}" \
-passout pass:"${P12_PASSWORD}" \
-name "${FULL_NAME}"
if [ ! -f "${P12_FILE}" ]; then
echo -e "${RED}创建PKCS#12文件失败!${NC}"
else
chmod 400 "${P12_FILE}"
echo -e "${GREEN}PKCS#12文件创建成功${NC}"
fi
# 创建信息文件
INFO_FILE="${USER_DIR}/user-info.txt"
cat > "${INFO_FILE}" << EOF
用户证书信息
============
基本信息:
----------
用户名: ${USERNAME}
全名: ${FULL_NAME}
邮箱: ${EMAIL}
部门: ${DEPARTMENT}
组织: ${ORGANIZATION}
位置: ${LOCALITY}, ${STATE}, ${COUNTRY}
证书信息:
----------
创建日期: $(date)
有效期: 365天
PKCS#12密码: ${P12_PASSWORD}
文件清单:
----------
1. 私钥: private/${USERNAME}.key
2. 证书: certs/${USERNAME}.crt
3. 证书链: certs/${USERNAME}-chain.crt
4. CSR: certs/${USERNAME}.csr
5. PKCS#12: certs/${USERNAME}.p12
6. CA证书: certs/ca.crt (拷贝)
重要提醒:
----------
1. 私钥文件必须严格保密!
2. PKCS#12文件包含私钥,需安全传输
3. 证书过期前请及时续期
EOF
# 创建README文件
README_FILE="${USER_DIR}/README.txt"
cat > "${README_FILE}" << 'EOF'
使用说明
========
1. 导入证书到浏览器:
- 双击 .p12 文件
- 输入密码导入
- 可用于HTTPS客户端认证
2. 验证证书:
openssl verify -CAfile certs/ca.crt certs/用户名.crt
3. 查看证书信息:
openssl x509 -in certs/用户名.crt -text -noout
4. 证书续期:
联系管理员重新签发证书
安全注意事项:
1. 私钥文件 (.key) 必须严格保密
2. 不要共享PKCS#12密码
3. 证书丢失立即报告
EOF
# 复制CA证书到用户目录
cp ${ROOT_CA_DIR}/certs/ca.crt "${USER_DIR}/certs/ca.crt"
# 记录到数据库
EXPIRY_DATE=$(date -d "+365 days" "+%Y-%m-%d")
SERIAL=$(openssl x509 -in "${USER_CERT}" -noout -serial | cut -d= -f2)
echo "\"${USERNAME}\",\"${EMAIL}\",\"${DEPARTMENT}\",\"${ORGANIZATION}\",\"${COUNTRY}\",\"${STATE}\",\"${LOCALITY}\",\"$(date '+%Y-%m-%d')\",\"${EXPIRY_DATE}\",\"${SERIAL}\",\"ACTIVE\"" >> ${DB_FILE}
# 打包用户证书
ZIP_FILE="${USERS_DIR}/${USERNAME}-certificates.zip"
echo -e "${BLUE}打包用户证书...${NC}"
cd "${USER_DIR}"
zip -q -r "${ZIP_FILE}" .
cd - > /dev/null
chmod 400 "${ZIP_FILE}"
# 验证证书
echo -e "${BLUE}验证证书...${NC}"
if openssl verify -CAfile "${USER_DIR}/certs/ca.crt" "${USER_CERT}" > /dev/null 2>&1; then
echo -e "${GREEN}证书验证成功!${NC}"
else
echo -e "${YELLOW}证书验证失败,但文件已生成${NC}"
fi
# 显示证书信息
echo -e "${CYAN}证书信息摘要:${NC}"
openssl x509 -in "${USER_CERT}" -noout -subject -issuer -dates
echo -e "${GREEN}用户证书创建完成!${NC}"
echo -e "${YELLOW}用户目录: ${USER_DIR}${NC}"
echo -e "${YELLOW}打包文件: ${ZIP_FILE}${NC}"
echo -e "${YELLOW}PKCS#12密码: ${P12_PASSWORD}${NC}"
# 显示文件列表
echo -e "\n${CYAN}生成的文件:${NC}"
echo "私钥目录:"
ls -la "${USER_DIR}/private/"
echo -e "\n证书目录:"
ls -la "${USER_DIR}/certs/"
# 清理临时文件
rm -f /tmp/user-csr.cnf /tmp/signing.cnf
}
# 列出所有用户
list_users() {
echo -e "${GREEN}用户列表:${NC}"
if [ ! -d "${USERS_DIR}" ] || [ -z "$(ls -A ${USERS_DIR} 2>/dev/null)" ]; then
echo -e "${YELLOW}暂无用户${NC}"
return
fi
echo "用户名 | 邮箱 | 部门 | 创建时间 | 状态"
echo "----------------------------------------"
# 从数据库读取
if [ -f "${DB_FILE}" ]; then
tail -n +2 "${DB_FILE}" | while IFS=, read -r username email department organization country state city created expiry serial status
do
# 移除引号
username=$(echo "$username" | tr -d '"')
email=$(echo "$email" | tr -d '"')
department=$(echo "$department" | tr -d '"')
created=$(echo "$created" | tr -d '"')
status=$(echo "$status" | tr -d '"')
echo "$username | $email | $department | $created | $status"
done
else
# 从目录结构读取
for user_dir in ${USERS_DIR}/*/; do
if [ -d "$user_dir" ]; then
username=$(basename "$user_dir")
if [ -f "${user_dir}/user-info.txt" ]; then
email=$(grep "邮箱:" "${user_dir}/user-info.txt" | cut -d: -f2 | xargs)
department=$(grep "部门:" "${user_dir}/user-info.txt" | cut -d: -f2 | xargs)
echo "$username | $email | $department | $(stat -c %y "$user_dir" | cut -d' ' -f1) | ACTIVE"
else
echo "$username | N/A | N/A | $(stat -c %y "$user_dir" | cut -d' ' -f1) | UNKNOWN"
fi
fi
done
fi
}
# 查看用户详情
view_user() {
echo -e "${GREEN}查看用户详情${NC}"
read -p "输入用户名: " USERNAME
USER_DIR="${USERS_DIR}/${USERNAME}"
if [ ! -d "${USER_DIR}" ]; then
echo -e "${RED}用户不存在!${NC}"
return 1
fi
echo -e "${CYAN}用户信息:${NC}"
if [ -f "${USER_DIR}/user-info.txt" ]; then
cat "${USER_DIR}/user-info.txt"
else
echo "基本信息文件不存在"
fi
echo -e "\n${CYAN}证书状态:${NC}"
if [ -f "${USER_DIR}/certs/${USERNAME}.crt" ]; then
openssl x509 -in "${USER_DIR}/certs/${USERNAME}.crt" -noout -subject -issuer -dates
fi
echo -e "\n${CYAN}文件列表:${NC}"
ls -la "${USER_DIR}/"
ls -la "${USER_DIR}/private/" 2>/dev/null || echo "私钥目录不存在"
ls -la "${USER_DIR}/certs/" 2>/dev/null || echo "证书目录不存在"
}
# 吊销用户证书
revoke_user_cert() {
echo -e "${GREEN}吊销用户证书${NC}"
read -p "输入用户名: " USERNAME
USER_DIR="${USERS_DIR}/${USERNAME}"
if [ ! -d "${USER_DIR}" ]; then
echo -e "${RED}用户不存在!${NC}"
return 1
fi
USER_CERT="${USER_DIR}/certs/${USERNAME}.crt"
if [ ! -f "${USER_CERT}" ]; then
echo -e "${RED}证书文件不存在!${NC}"
return 1
fi
echo -e "${YELLOW}确认吊销用户 ${USERNAME} 的证书? (y/N)${NC}"
read -r response
if [[ ! "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
return
fi
CA_PASSWORD=$(get_ca_password)
# 创建吊销配置文件
cat > /tmp/revoke.cnf << EOF
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = ${ROOT_CA_DIR}
certs = \$dir/certs
crl_dir = \$dir/crl
new_certs_dir = \$dir/newcerts
database = \$dir/index.txt
serial = \$dir/serial
RANDFILE = \$dir/private/.rand
certificate = \$dir/certs/ca.crt
private_key = \$dir/private/ca.key
EOF
if [ -z "$CA_PASSWORD" ]; then
openssl ca -config /tmp/revoke.cnf -revoke "${USER_CERT}"
else
openssl ca -config /tmp/revoke.cnf -revoke "${USER_CERT}" -passin pass:"${CA_PASSWORD}"
fi
# 更新数据库状态
if [ -f "${DB_FILE}" ]; then
sed -i "/\"${USERNAME}\",/s/\"ACTIVE\"/\"REVOKED\"/" "${DB_FILE}"
fi
# 移动文件到备份目录
BACKUP_DIR="${USER_DIR}/backup/revoked-$(date +%Y%m%d-%H%M%S)"
mkdir -p "${BACKUP_DIR}"
mv "${USER_DIR}/certs/"*.crt "${BACKUP_DIR}/" 2>/dev/null || true
mv "${USER_DIR}/private/"*.key "${BACKUP_DIR}/" 2>/dev/null || true
echo -e "${GREEN}证书已吊销!${NC}"
# 生成CRL
echo -e "${BLUE}生成新的CRL...${NC}"
if [ -z "$CA_PASSWORD" ]; then
openssl ca -gencrl -out ${ROOT_CA_DIR}/crl/ca.crl -config /tmp/revoke.cnf
else
openssl ca -gencrl -out ${ROOT_CA_DIR}/crl/ca.crl -config /tmp/revoke.cnf -passin pass:"${CA_PASSWORD}"
fi
rm -f /tmp/revoke.cnf
}
# 验证证书
verify_cert() {
echo -e "${GREEN}验证证书${NC}"
read -p "输入用户名: " USERNAME
USER_DIR="${USERS_DIR}/${USERNAME}"
if [ ! -d "${USER_DIR}" ]; then
echo -e "${RED}用户不存在!${NC}"
return 1
fi
USER_CERT="${USER_DIR}/certs/${USERNAME}.crt"
CA_CERT="${USER_DIR}/certs/ca.crt"
if [ ! -f "${USER_CERT}" ]; then
echo -e "${RED}证书文件不存在!${NC}"
return 1
fi
if [ ! -f "${CA_CERT}" ]; then
echo -e "${RED}CA证书文件不存在!${NC}"
return 1
fi
echo -e "${CYAN}验证结果:${NC}"
if openssl verify -CAfile "${CA_CERT}" "${USER_CERT}"; then
echo -e "${GREEN}证书有效!${NC}"
else
echo -e "${RED}证书无效!${NC}"
fi
}
# 显示帮助信息
show_help() {
echo -e "${GREEN}CA 管理器 v4.2${NC}"
echo "==============="
echo "功能:"
echo " - 每个用户独立文件夹管理"
echo " - RSA 3072 + SHA-512加密"
echo " - 完整的证书生命周期管理"
echo " - 支持密码保护的CA私钥"
echo ""
echo "目录结构:"
echo " ~/my-ca/"
echo " ├── root-ca/ # CA根证书"
echo " ├── users/ # 用户证书目录"
echo " │ └── username/ # 每个用户独立目录"
echo " │ ├── private/ # 私钥"
echo " │ ├── certs/ # 证书文件"
echo " │ ├── backup/ # 备份"
echo " │ ├── user-info.txt"
echo " │ └── README.txt"
echo " └── cert-database.csv # 证书数据库"
}
# 主菜单
main_menu() {
clear
echo -e "${PURPLE}=================================${NC}"
echo -e "${PURPLE} CA 管理器 v4.2 ${NC}"
echo -e "${PURPLE} RSA 3072 | SHA-512 ${NC}"
echo -e "${PURPLE}=================================${NC}"
echo ""
while true; do
echo -e "${CYAN}请选择操作:${NC}"
echo "1) 初始化CA环境"
echo "2) 创建根CA"
echo "3) 创建用户证书"
echo "4) 列出所有用户"
echo "5) 查看用户详情"
echo "6) 吊销用户证书"
echo "7) 验证证书"
echo "8) 显示帮助"
echo "9) 退出"
echo ""
read -p "请输入选择 (1-9): " choice
case $choice in
1)
init_directories
generate_config_files
;;
2)
create_root_ca
;;
3)
create_user_cert
;;
4)
list_users
;;
5)
view_user
;;
6)
revoke_user_cert
;;
7)
verify_cert
;;
8)
show_help
;;
9)
echo -e "${GREEN}再见!${NC}"
exit 0
;;
*)
echo -e "${RED}无效的选择!${NC}"
;;
esac
echo ""
read -p "按回车键继续..."
clear
done
}
# 启动脚本
if [ "$1" = "--init" ]; then
init_directories
generate_config_files
echo -e "${GREEN}初始化完成!${NC}"
echo -e "${YELLOW}用户证书将存放在: ${USERS_DIR}/${NC}"
elif [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
show_help
else
# 检查是否已初始化
if [ ! -d "${CONFIG_DIR}" ]; then
echo -e "${YELLOW}CA环境未初始化!${NC}"
echo -e "请先运行: $0 --init"
exit 1
fi
main_menu
fi
配置Nginx服务
生成服务端证书
openssl req -newkey rsa:3072 -nodes -keyout srv.key \ -subj "/C=CN/ST=Beijing/L=Beijing/O=Winter/CN=YourIP/Domain" \ -addext "subjectAltName = DNS:127.0.0.1" \ -addext "keyUsage = digitalSignature, keyEncipherment" \ -addext "extendedKeyUsage = serverAuth, clientAuth" \ -sha512 | \ openssl x509 -req -days 365 -sha512 \ -CA root-ca/certs/ca.crt \ -CAkey root-ca/private/ca.key \ -CAcreateserial \ -out srv.crtNginx 配置信息
[root@localhost cert]# cat /etc/nginx/nginx.conf # For more information on configuration, see: # * Official English Documentation: http://nginx.org/en/docs/ # * Official Russian Documentation: http://nginx.org/ru/docs/ user nginx; worker_processes auto; error_log /var/log/nginx/error.log; pid /run/nginx.pid; # Load dynamic modules. See /usr/share/doc/nginx/README.dynamic. include /usr/share/nginx/modules/*.conf; events { worker_connections 1024; } http { log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 4096; include /etc/nginx/mime.types; default_type application/octet-stream; # Load modular configuration files from the /etc/nginx/conf.d directory. # See http://nginx.org/en/docs/ngx_core_module.html#include # for more information. include /etc/nginx/conf.d/*.conf; server { listen 443 ssl; listen [::]:443 ssl; server_name _; root /usr/share/nginx/html; # Load configuration files for the default server block. include /etc/nginx/default.d/*.conf; ssl_certificate /etc/nginx/cert/3interserver.crt; ssl_certificate_key /etc/nginx/cert/3interserver.key; # CA证书用于验证客户端 ssl_client_certificate /etc/nginx/cert/ca.crt; ssl_verify_client on; # 关键:开启客户端验证 ssl_verify_depth 2; error_page 404 /404.html; location = /404.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } }
效果
不带客户端证书访问

命令行带客户端证书访问
curl -v \ ─╯ --cert winter.crt \ --key ../private/winter.key \ --cacert ca.crt \ https://10.211.55.8