Files
certificate-management/ca-server/internal/handlers/certificate_handler.go

318 lines
8.1 KiB
Go
Raw Permalink Normal View History

2025-07-23 17:30:33 +08:00
package handlers
import (
"ca-mini/db"
"ca-mini/internal/logger"
"ca-mini/internal/model"
"ca-mini/internal/repository"
"ca-mini/pkg/utils"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
"github.com/gorilla/mux"
)
const (
// 错误码
ERROR_CODE_SUCCESS = 0
ERROR_CODE_FAIL = 1
// 错误信息
ERROR_MSG_SUCCESS = "success"
ERROR_MSG_FAIL = "fail"
WORK_PATH = "/opt/arrokoth/ca-mini"
CA_CERT_PATH = WORK_PATH + "/ca/CaRoot.crt"
CA_CONFIG_PATH = WORK_PATH + "/ca/ca.conf"
CRL_FILE_PATH = WORK_PATH + "/crl/CaRoot.crl"
)
type ErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Date string `json:"date"`
}
type SuccessResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Date string `json:"date"`
}
type CertificateResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Date string `json:"date"`
Data model.CertInfo `json:"data"`
}
// IssueCertificate 处理证书签发请求
func IssueCertificate(w http.ResponseWriter, r *http.Request) {
// 解析CSR请求
var csrRequest struct {
Subject string `json:"subject"`
SubjectAltName string `json:"subjectAltName"`
Alg string `json:"alg"`
Len int `json:"len"`
}
if err := json.NewDecoder(r.Body).Decode(&csrRequest); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// 检查Subject是否为空
if csrRequest.Subject == "" {
http.Error(w, "Subject is required", http.StatusBadRequest)
return
}
// 生成密钥
id, err := utils.GenerateKey(csrRequest.Alg, csrRequest.Len)
if err != nil {
http.Error(w, "Failed to generate key", http.StatusInternalServerError)
return
}
var sans []string
if csrRequest.SubjectAltName != "" {
sans = strings.Split(strings.TrimSpace(csrRequest.SubjectAltName), ",")
}
// 如果SubjectAltName为空则使用Subject作为SAN
if len(sans) == 0 {
sans = []string{csrRequest.Subject}
}
// 生成CSR文件
id, err = utils.GenerateCsr(id, csrRequest.Subject, sans)
if err != nil {
http.Error(w, "Failed to generate CSR file", http.StatusInternalServerError)
return
}
// 生成证书
id, err = utils.GenerateCert(id, 0)
if err != nil {
http.Error(w, "Failed to generate certificate", http.StatusInternalServerError)
return
}
// 解析证书信息
certInfo, err := ParseCert(id)
if err != nil {
http.Error(w, "Failed to parse certificate", http.StatusInternalServerError)
return
}
// 新增证书到数据库
repository := repository.NewCertificateRepository(db.DB)
if err := repository.AddCert(certInfo); err != nil {
logger.Error("Failed to save certificate to database %v", err)
http.Error(w, "Failed to save certificate", http.StatusInternalServerError)
return
}
// 返回证书信息
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
response := CertificateResponse{
Code: http.StatusOK,
Message: "Certificate issued successfully",
Date: time.Now().Format(time.RFC3339),
Data: *certInfo,
}
json.NewEncoder(w).Encode(response)
}
// 查询证书
func GetCertificate(w http.ResponseWriter, r *http.Request) {
// 从URL查询参数中获取id
//id := r.URL.Query().Get("id")
// 获取路径参数 id
vars := mux.Vars(r)
id := vars["id"]
if id == "" {
http.Error(w, "id is required", http.StatusBadRequest)
return
}
// 新增证书到数据库
repository := repository.NewCertificateRepository(db.DB)
certInfo, err := repository.SelectCertById(id)
if err != nil {
logger.Error("Failed to query certificate to database %v", err)
http.Error(w, "Failed to query certificate", http.StatusInternalServerError)
return
}
// 读取CA证书文件内容
caData, err := utils.GetCaCert()
if err == nil {
certInfo.Ca = caData
}
// 返回证书信息
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
response := CertificateResponse{
Code: http.StatusOK,
Message: "Certificate retrieved successfully",
Date: time.Now().Format(time.RFC3339),
Data: *certInfo,
}
json.NewEncoder(w).Encode(response)
}
// CheckBlacklist 处理黑名单查询请求
func CheckBlacklist(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]bool{
"isBlacklisted": false,
})
}
// RevokeCertificate 处理证书撤销请求
func RevokeCertificate(w http.ResponseWriter, r *http.Request) {
// 从URL查询参数中获取id
// id := r.URL.Query().Get("id")
// 获取路径参数 id
vars := mux.Vars(r)
id := vars["id"]
if id == "" {
http.Error(w, "id is required", http.StatusBadRequest)
return
}
// 检查管理员权限假设通过HTTP Basic Auth验证
username, password, ok := r.BasicAuth()
if !ok || username != "admin" || password != "admin" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// 解析证书信息
id, err := utils.RevokeCert(id)
if err != nil {
http.Error(w, "Failed to revoke certificate", http.StatusInternalServerError)
return
}
// 返回证书信息
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
response := SuccessResponse{
Code: http.StatusOK,
Message: id,
Date: time.Now().Format(time.RFC3339),
}
json.NewEncoder(w).Encode(response)
}
// 解析证书
func ParseCert(id string) (*model.CertInfo, error) {
// 读取私钥文件内容
keyData, err := utils.GetKey(id)
if err != nil {
return nil, fmt.Errorf("获取私钥失败: %v", err)
}
// 读取证书请求文件内容
csrData, err := utils.GetCsr(id)
if err != nil {
return nil, fmt.Errorf("获取证书请求失败: %v", err)
}
// 读取CA证书文件内容
caData, err := utils.GetCaCert()
if err != nil {
return nil, fmt.Errorf("获取CA证书失败: %v", err)
}
// 读取证书文件内容
certData, err := utils.GetCert(id)
if err != nil {
return nil, fmt.Errorf("获取证书失败: %v", err)
}
// 解析PEM编码的证书
block, _ := pem.Decode([]byte(certData))
if block == nil {
return nil, fmt.Errorf("无法解析PEM块")
}
// 解析X.509证书
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, fmt.Errorf("解析证书失败: %v", err)
}
version := fmt.Sprintf("%d", cert.Version)
certCn := cert.Subject.CommonName
certDn := cert.Subject.String()
issuerCn := cert.Issuer.CommonName
issuerDn := cert.Issuer.String()
publicKeyAlg := cert.PublicKeyAlgorithm.String()
signatureAlg := cert.SignatureAlgorithm.String()
certSubAltName := ""
for i, DNSName := range cert.DNSNames {
if i == 0 {
certSubAltName = DNSName
} else {
certSubAltName = fmt.Sprintf("%s,%s", certSubAltName, DNSName)
}
}
// beforeTime := cert.NotBefore.Format(time.RFC3339)
// afterTime := cert.NotAfter.Format(time.RFC3339)
beforeTime := cert.NotBefore
afterTime := cert.NotAfter
serialNumber := cert.SerialNumber.String()
return &model.CertInfo{
CertId: id,
Version: version,
CertCn: certCn,
CertDn: certDn,
PublicKeyAlg: publicKeyAlg,
SignatureAlg: signatureAlg,
IssuerCn: issuerCn,
IssuerDn: issuerDn,
SerialNumber: serialNumber,
CertSubAltName: certSubAltName,
BeforeTime: beforeTime,
AfterTime: afterTime,
PrivateKey: keyData,
Csr: csrData,
Cert: certData,
Ca: caData,
}, nil
}
// DownloadCRL 处理黑名单文件下载请求
func DownloadCRL(w http.ResponseWriter, r *http.Request) {
// 打开黑名单文件
filePath := CRL_FILE_PATH
file, err := os.Open(filePath)
if err != nil {
http.Error(w, "Failed to open blacklist file", http.StatusInternalServerError)
return
}
defer file.Close()
// 设置响应头,触发浏览器下载行为
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", "attachment; filename=CaRoot.crl")
// 将文件内容写入响应体
if _, err := io.Copy(w, file); err != nil {
http.Error(w, "Error streaming the file", http.StatusInternalServerError)
return
}
}