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

414 lines
9.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

### 一、安装CFSSL工具
CFSSLCloudFlare's PKI Toolkit是一个开源的PKI工具集可用于创建私有CA和证书。
1. **Linux/macOS**
```bash
# 下载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/
```
2. **Windows**
- 从 [CFSSL Releases](https://github.com/cloudflare/cfssl/releases) 下载对应版本的 `cfssl.exe``cfssljson.exe`
- 将可执行文件添加到系统PATH路径
### 二、创建私有CA
#### 1. 配置CA证书
创建一个名为 `ca-config.json` 的文件,定义证书的有效期和使用策略:
```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` 文件:
```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证书
```bash
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
```
这将生成三个文件:
- `ca.pem`CA公钥证书
- `ca-key.pem`CA私钥妥善保管不要泄露
- `ca.csr`CA证书签名请求
### 三、使用CA签署服务器证书
#### 1. 创建服务器证书CSR配置
创建 `server-csr.json` 文件:
```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直接签署服务器证书
```bash
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` 文件:
```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. 生成客户端证书和私钥
```bash
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. 验证服务器证书
```bash
openssl verify -CAfile ca.pem server.pem
```
如果输出 `server.pem: OK`,则证书有效
#### 2. 验证客户端证书
```bash
openssl verify -CAfile ca.pem client.pem
```
### 六、证书使用示例
#### 1. 在HTTPS服务器中使用
`server.pem``server-key.pem` 配置到你的Web服务器如Nginx、Apache
```nginx
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服务器
```bash
curl --cacert ca.pem https://server.example.com
```
### 七、安全注意事项
1. **私钥保护**
- `ca-key.pem``server-key.pem``client-key.pem` 是敏感文件,应存储在安全位置
- 限制访问权限:`chmod 400 *.key.pem`
2. **证书备份**
- 定期备份CA证书和私钥
- 考虑使用硬件安全模块(HSM)存储CA私钥
3. **证书撤销**
- 如需撤销证书可使用CFSSL生成证书撤销列表(CRL)
- 更新 `ca-config.json` 添加CRL配置
通过以上步骤你已成功创建了一个有效期10年的私有CA并使用它签署了服务器和客户端证书。
### 八、附快速签发脚本
```bash
#!/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
```