Files
article/私有CA.md
2025-07-30 21:09:30 +08:00

9.8 KiB
Raw Permalink Blame History

一、安装CFSSL工具

CFSSLCloudFlare's PKI Toolkit是一个开源的PKI工具集可用于创建私有CA和证书。

  1. Linux/macOS
# 下载cfssl和cfssljson工具
curl -o cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
curl -o cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
chmod +x cfssl cfssljson
sudo mv cfssl cfssljson /usr/local/bin/
  1. Windows
    • CFSSL Releases 下载对应版本的 cfssl.execfssljson.exe
    • 将可执行文件添加到系统PATH路径

二、创建私有CA

1. 配置CA证书

创建一个名为 ca-config.json 的文件,定义证书的有效期和使用策略:

{
  "signing": {
    "default": {
      "expiry": "87600h"  // 10年有效期
    },
    "profiles": {
      "server": {
        "expiry": "87600h",
        "usages": ["signing", "key encipherment", "server auth"]
      },
      "client": {
        "expiry": "87600h",
        "usages": ["signing", "key encipherment", "client auth"]
      },
      "peer": {
        "expiry": "87600h",
        "usages": ["signing", "key encipherment", "server auth", "client auth"]
      }
    }
  }
}

2. 创建CA证书签名请求(CSR)配置

创建 ca-csr.json 文件:

{
  "CN": "My Private CA",
  "key": {
    "algo": "rsa",
    "size": 4096
  },
  "names": [
    {
      "C": "CN",
      "ST": "Shanghai",
      "L": "Shanghai",
      "O": "My Organization",
      "OU": "IT Department"
    }
  ],
  "ca": {
    "expiry": "87600h"  // CA证书有效期10年
  }
}

3. 生成CA证书和私钥

执行以下命令生成自签名CA证书

cfssl gencert -initca ca-csr.json | cfssljson -bare ca

这将生成三个文件:

  • ca.pemCA公钥证书
  • ca-key.pemCA私钥妥善保管不要泄露
  • ca.csrCA证书签名请求

三、使用CA签署服务器证书

1. 创建服务器证书CSR配置

创建 server-csr.json 文件:

{
  "CN": "server.example.com",
  "hosts": [
    "server.example.com",
    "192.168.1.100",
    "localhost",
    "127.0.0.1"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Shanghai",
      "L": "Shanghai",
      "O": "My Organization",
      "OU": "IT Department"
    }
  ]
}

注意:hosts 字段必须包含服务器的域名、IP地址以及任何需要访问的别名

2. 生成服务器证书和私钥

使用CA直接签署服务器证书

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=server \
  server-csr.json | cfssljson -bare server

这将生成:

  • server.pem:服务器公钥证书
  • server-key.pem:服务器私钥

四、使用CA签署客户端证书

1. 创建客户端证书CSR配置

创建 client-csr.json 文件:

{
  "CN": "client.example.com",
  "hosts": [],  // 客户端证书通常不需要指定hosts
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Shanghai",
      "L": "Shanghai",
      "O": "My Organization",
      "OU": "IT Department"
    }
  ]
}

2. 生成客户端证书和私钥

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=client \
  client-csr.json | cfssljson -bare client

这将生成:

  • client.pem:客户端公钥证书
  • client-key.pem:客户端私钥

五、证书验证

1. 验证服务器证书

openssl verify -CAfile ca.pem server.pem

如果输出 server.pem: OK,则证书有效

2. 验证客户端证书

openssl verify -CAfile ca.pem client.pem

六、证书使用示例

1. 在HTTPS服务器中使用

server.pemserver-key.pem 配置到你的Web服务器如Nginx、Apache

server {
    listen 443 ssl;
    server_name server.example.com;

    ssl_certificate /path/to/server.pem;
    ssl_certificate_key /path/to/server-key.pem;
    ssl_client_certificate /path/to/ca.pem;  # 客户端证书验证(可选)
    ssl_verify_client on;                    # 启用客户端证书验证(可选)
    
    # 其他配置...
}

2. 在客户端应用中使用

在需要验证服务器证书的客户端应用中,导入 ca.pem 作为信任的根证书。例如使用curl访问HTTPS服务器

curl --cacert ca.pem https://server.example.com

七、安全注意事项

  1. 私钥保护

    • ca-key.pemserver-key.pemclient-key.pem 是敏感文件,应存储在安全位置
    • 限制访问权限:chmod 400 *.key.pem
  2. 证书备份

    • 定期备份CA证书和私钥
    • 考虑使用硬件安全模块(HSM)存储CA私钥
  3. 证书撤销

    • 如需撤销证书可使用CFSSL生成证书撤销列表(CRL)
    • 更新 ca-config.json 添加CRL配置

通过以上步骤你已成功创建了一个有效期10年的私有CA并使用它签署了服务器和客户端证书。

八、附快速签发脚本

#!/bin/bash

# 证书快速签发脚本
# 依赖: cfssl, cfssljson 已安装并初始化好CA证书

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color

# 默认配置
CA_KEY="/root/ckw/cfssl/ca/ca-ecdsa-key.pem"
CA_CERT="/root/ckw/cfssl/ca/ca-ecdsa.pem"
CA_CONFIG="/root/ckw/cfssl/ca/ca-config.json"
OUTPUT_DIR="certs"
MERGE_CA="true"  # 默认合并根证书

# 使用帮助
function show_help {
    echo -e "${GREEN}证书快速签发脚本${NC}"
    echo "用法: $0 [选项]"
    echo "选项:"
    echo "  -h, --help            显示此帮助信息"
    echo "  -n, --name NAME       证书名称 (必填)"
    echo "  -t, --type TYPE       证书类型: server, client, peer (默认: server)"
    echo "  -c, --ca CA_CERT      CA证书路径 (默认: $CA_CERT)"
    echo "  -k, --ca-key CA_KEY   CA私钥路径 (默认: $CA_KEY)"
    echo "  -C, --ca-config CONF  CA配置文件路径 (默认: $CA_CONFIG)"
    echo "  -o, --output DIR      输出目录 (默认: $OUTPUT_DIR)"
    echo "  -d, --domains LIST    域名列表 (逗号分隔)"
    echo ""
    echo "示例:"
    echo "  $0 -n server1 -d example.com,www.example.com "
}

# 参数解析
NAME=""
TYPE="server"
DOMAINS=""

while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--help)
            show_help
            exit 0
            ;;
        -n|--name)
            NAME="$2"
            shift 2
            ;;
        -t|--type)
            TYPE="$2"
            shift 2
            ;;
        -c|--ca)
            CA_CERT="$2"
            shift 2
            ;;
        -k|--ca-key)
            CA_KEY="$2"
            shift 2
            ;;
        -C|--ca-config)
            CA_CONFIG="$2"
            shift 2
            ;;
        -o|--output)
            OUTPUT_DIR="$2"
            shift 2
            ;;
        -d|--domains)
            DOMAINS="$2"
            shift 2
            ;;
        --no-merge-ca)
            MERGE_CA="false"
            shift
            ;;
        *)
            echo -e "${RED}未知参数: $1${NC}" >&2
            show_help
            exit 1
            ;;
    esac
done

# 验证必填参数
if [[ -z "$NAME" ]]; then
    echo -e "${RED}错误: 必须指定证书名称 (-n/--name)${NC}" >&2
    show_help
    exit 1
fi

# 验证证书类型
if [[ "$TYPE" != "server" && "$TYPE" != "client" && "$TYPE" != "peer" ]]; then
    echo -e "${RED}错误: 证书类型必须是 server, client 或 peer${NC}" >&2
    exit 1
fi

# 验证文件是否存在
for file in "$CA_CERT" "$CA_KEY" "$CA_CONFIG"; do
    if [[ ! -f "$file" ]]; then
        echo -e "${RED}错误: 文件 $file 不存在${NC}" >&2
        exit 1
    fi
done

# 当类型为server且未指定域名时默认将name作为域名
if [[ "$TYPE" == "server" && -z "$DOMAINS" ]]; then
    DOMAINS="$NAME"
    echo -e "${YELLOW}注意: 证书类型为server且未指定域名默认添加 ${NAME} 作为域名${NC}"
fi


# 创建证书单独目录
CERT_DIR="$OUTPUT_DIR/$NAME"
mkdir -p "$CERT_DIR" || { echo -e "${RED}无法创建证书目录: $CERT_DIR${NC}"; exit 1; }

# 生成证书签名请求配置
CSR_CONFIG="$CERT_DIR/${NAME}-csr.json"
cat > "$CSR_CONFIG" <<EOF
{
  "CN": "$NAME",
  "key": {
    "algo": "ecdsa",
    "size": 256
  },
  "names": [
    {
      "C": "CN",
      "ST": "Beijing",
      "L": "Beijing",
      "O": "CUA",
      "OU": "IT"
    }
  ]
}
EOF

# 生成SAN列表
SAN_LIST=""
if [[ -n "$DOMAINS" ]]; then
    SAN_LIST+="$DOMAINS,"
fi

# 移除末尾逗号
SAN_LIST="${SAN_LIST%,}"

# 根据证书类型选择profile
case "$TYPE" in
    server)
        PROFILE="server"
        ;;
    client)
        PROFILE="client"
        ;;
    peer)
        PROFILE="peer"
        ;;
esac

# 生成证书
echo -e "${YELLOW}正在生成 $TYPE 证书: $NAME${NC}"
echo -e "${YELLOW}SAN列表: $SAN_LIST${NC}"

cfssl gencert \
  -ca="$CA_CERT" \
  -ca-key="$CA_KEY" \
  -config="$CA_CONFIG" \
  -profile="$PROFILE" \
  ${SAN_LIST:+-hostname="$SAN_LIST"} \
  "$CSR_CONFIG" | cfssljson -bare "$CERT_DIR/$NAME"

# 验证生成结果
if [[ -f "$CERT_DIR/${NAME}.pem" && -f "$CERT_DIR/${NAME}-key.pem" ]]; then
    echo -e "${GREEN}证书生成成功!${NC}"
    echo -e "${GREEN}证书路径: ${CERT_DIR}/${NAME}.pem${NC}"
    echo -e "${GREEN}私钥路径: ${CERT_DIR}/${NAME}-key.pem${NC}"
    echo -e "${GREEN}证书签名请求: ${CSR_CONFIG}${NC}"

    # 合并根证书
    if [[ "$MERGE_CA" == "true" ]]; then
        FULL_CHAIN="${CERT_DIR}/${NAME}-fullchain.pem"
        cat "$CERT_DIR/${NAME}.pem" "$CA_CERT" > "$FULL_CHAIN"
        echo -e "${GREEN}合并后的完整证书链: ${FULL_CHAIN}${NC}"
    fi

    # 显示证书信息
    echo -e "\n${YELLOW}证书信息:${NC}"
    openssl x509 -noout -text -in "$CERT_DIR/${NAME}.pem" | grep -A 5 "Subject Alternative Name"
else
    echo -e "${RED}证书生成失败!${NC}"
    exit 1
fi