Files
certificate-management/ca-server/pkg/utils/ca_util.go
wangjianhong 5e4e272b3a init
2025-07-23 17:30:33 +08:00

322 lines
7.5 KiB
Go

package utils
import (
"bytes"
"crypto/rand"
"fmt"
"io"
"os"
"os/exec"
"time"
)
const (
OPENSSL_PATH = "openssl"
// WORK_PATH = "/opt/arrokoth/ca-mini"
WORK_PATH = "/home/code/git/ca-mini/ca-server"
CA_CERT = WORK_PATH + "/ca/CaRoot.crt"
CA_CONFIG = WORK_PATH + "/ca/ca.conf"
CERT_PATH = WORK_PATH + "/cert"
CRL = WORK_PATH + "/crl/CaRoot.crl"
)
// GenerateRandomString 生成指定长度的随机字符串
func GenerateRandomString(length int) (string, error) {
// 创建一个字节切片,用于存储随机数据
bytes := make([]byte, length)
// 使用crypto/rand包生成随机字节
_, err := io.ReadFull(rand.Reader, bytes)
if err != nil {
return "", fmt.Errorf("failed to generate random string: %v", err)
}
// 将随机字节转换为Base64字符串
// randomString := base64.RawURLEncoding.EncodeToString(bytes)
// 将随机字节转换为十六进制字符串
randomString := fmt.Sprintf("%x", bytes)
return randomString, nil
}
// 根据时间戳+随机数生成唯一ID
func GenerateUniqueID() (string, error) {
// 获取当前时间戳
timestamp := time.Now().UnixNano()
// 生成随机数
randomNumber, err := GenerateRandomString(8)
if err != nil {
return "", err
}
// 拼接时间戳和随机数
uniqueID := fmt.Sprintf("%d%s", timestamp, randomNumber)
// uniqueID := fmt.Sprintf("%d%s", timestamp, uuid.New().String())
return uniqueID, nil
}
func GetKeyFilePath(keyId string) string {
return CERT_PATH + "/" + keyId + ".key"
}
func GetCsrFilePath(keyId string) string {
return CERT_PATH + "/" + keyId + ".csr"
}
func GetCertFilePath(subject string) string {
return CERT_PATH + "/" + subject + ".crt"
}
// 生成密钥
func GenerateKey(alg string, len int) (string, error) {
// 生成私钥
// openssl genrsa -out Ca.key 2048
var keyAlg string
var keyLen string
id, err := GenerateUniqueID()
if err != nil {
return "", err
}
keyFilePath := GetKeyFilePath(id)
switch alg {
case "rsa":
keyAlg = "rsa"
case "ec":
keyAlg = "ec"
default:
keyAlg = "rsa"
}
switch len {
case 1024:
keyLen = "1024"
case 2048:
keyLen = "2048"
case 4096:
keyLen = "4096"
default:
keyLen = "2048"
}
// 创建命令
xcmd := exec.Command(OPENSSL_PATH, "gen"+keyAlg, "-out", keyFilePath, keyLen)
// 捕获标准输出和标准错误
var stdout, stderr bytes.Buffer
xcmd.Stdout = &stdout
xcmd.Stderr = &stderr
// 执行命令
err = xcmd.Run()
if err != nil {
return "", fmt.Errorf("命令执行失败: %v\n错误输出: %s", err, stderr.String())
}
return id, nil
}
// 生成证书请求
func GenerateCsr(id string, subject string, sans []string) (string, error) {
// 生成证书请求
// openssl req -new -key Ca.key -out Ca.csr -subj "/C=CN/ST=BeiJing/L=BeiJing/O=xxx/OU=dev/CN=CaRoot/emailAddress=CaRoot@xxx.com"
keyFilePath := GetKeyFilePath(id)
csrFilePath := GetCsrFilePath(id)
// 创建命令
// xcmd := exec.Command(OPENSSL_PATH, "req", "-new", "-key", keyFilePath, "-out", csrFilePath, "-subj", subject)
args := []string{"req", "-new", "-key", keyFilePath, "-out", csrFilePath, "-subj", subject}
// if len(sans) > 0 {
// sanStr := joinSans(sans)
// args = append(args, "-addext", "subjectAltName = "+sanStr)
// }
xcmd := exec.Command(OPENSSL_PATH, args...)
// 捕获标准输出和标准错误
var stdout, stderr bytes.Buffer
xcmd.Stdout = &stdout
xcmd.Stderr = &stderr
// 执行命令
err := xcmd.Run()
if err != nil {
return "", fmt.Errorf("命令执行失败: %v\n错误输出: %s", err, stderr.String())
}
return id, nil
}
// 生成证书
func GenerateCert(id string, days int) (string, error) {
// 生成证书
// openssl x509 -req -in Ca.csr -signkey Ca.key -out Ca.crt
// openssl ca -config ca.conf -notext -batch -in ./subordinate/Ca.csr -out ./subordinate/Ca.crt
csrFilePath := GetCsrFilePath(id)
certFilePath := GetCertFilePath(id)
xargs := []string{
"ca",
"-config", CA_CONFIG,
"-notext",
"-batch",
"-in", csrFilePath,
"-out", certFilePath,
}
if days > 0 && days < 3650 {
xargs = append(xargs, "-days", fmt.Sprintf("%d", days))
}
// 创建命令
xcmd := exec.Command(OPENSSL_PATH, xargs...)
// 捕获标准输出和标准错误
var stdout, stderr bytes.Buffer
xcmd.Stdout = &stdout
xcmd.Stderr = &stderr
// 执行命令
err := xcmd.Run()
if err != nil {
return "", fmt.Errorf("命令执行失败: %v\n错误输出: %s", err, stderr.String())
}
return id, nil
}
// 生成黑名单
func GenerateCrl(id string) (string, error) {
// 生成黑名单
// openssl ca -gencrl -out crl.pem -config ca.conf
xargs := []string{
"ca",
"-config", CA_CONFIG,
"-gencrl",
"-out", CRL,
}
// 创建命令
xcmd := exec.Command(OPENSSL_PATH, xargs...)
// 捕获标准输出和标准错误
var stdout, stderr bytes.Buffer
xcmd.Stdout = &stdout
xcmd.Stderr = &stderr
// 执行命令
err := xcmd.Run()
if err != nil {
return "", fmt.Errorf("命令执行失败: %v\n错误输出: %s", err, stderr.String())
}
return id, nil
}
// 撤销证书
func RevokeCert(id string) (string, error) {
// 撤销证书
// openssl ca -revoke ./subordinate/Ca.crt -config ca.conf
certFilePath := GetCertFilePath(id)
xargs := []string{
"ca",
"-config", CA_CONFIG,
"-revoke", certFilePath,
}
// 创建命令
xcmd := exec.Command(OPENSSL_PATH, xargs...)
// 捕获标准输出和标准错误
var stdout, stderr bytes.Buffer
xcmd.Stdout = &stdout
xcmd.Stderr = &stderr
// 执行命令
err := xcmd.Run()
if err != nil {
return "", fmt.Errorf("命令执行失败: %v\n错误输出: %s", err, stderr.String())
}
return id, nil
}
// 检查证书是否在黑名单中
func CheckCertInCrl(certFilePath string) (bool, error) {
// 检查证书是否在黑名单中
// openssl verify -CAfile /opt/arrokoth/ca-mini/ca/CaRoot.crt -CRLfile /opt/arrokoth/ca-mini/crl/CaRoot.crl /opt/arrokoth/ca-mini/cert/17455454097526357682Q7pIOUE70I.crt
// openssl crl -in crl.pem -noout -text
xargs := []string{
"crl",
"-in", certFilePath,
"-noout",
"-text",
}
// 创建命令
xcmd := exec.Command(OPENSSL_PATH, xargs...)
// 捕获标准输出和标准错误
var stdout, stderr bytes.Buffer
xcmd.Stdout = &stdout
xcmd.Stderr = &stderr
// 执行命令
err := xcmd.Run()
if err != nil {
return false, fmt.Errorf("命令执行失败: %v\n错误输出: %s", err, stderr.String())
}
return true, nil
}
// 读取文件内容
func ReadFile(filePath string) (string, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return "", fmt.Errorf("读取文件失败: %v", err)
}
// 将数据转换为字符串
content := string(data)
return content, nil
}
// 读取CA证书
func GetCaCert() (string, error) {
data, err := ReadFile(CA_CERT)
if err != nil {
return "", fmt.Errorf("读取CA证书失败: %v", err)
}
return data, nil
}
// 读取证书
func GetCert(id string) (string, error) {
data, err := ReadFile(GetCertFilePath(id))
if err != nil {
return "", fmt.Errorf("读取证书失败: %v", err)
}
return data, nil
}
// 读取私钥
func GetKey(id string) (string, error) {
data, err := ReadFile(GetKeyFilePath(id))
if err != nil {
return "", fmt.Errorf("读取私钥失败: %v", err)
}
return data, nil
}
// 读取证书请求
func GetCsr(id string) (string, error) {
data, err := ReadFile(GetCsrFilePath(id))
if err != nil {
return "", fmt.Errorf("读取证书请求失败: %v", err)
}
return data, nil
}
// joinSans 拼接 SAN 字段,例如 DNS:example.com, IP:192.168.0.1
func joinSans(sans []string) string {
result := ""
for i, san := range sans {
if i > 0 {
result += ", "
}
result += san
}
return result
}