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 } }