init
This commit is contained in:
317
ca-server/internal/handlers/certificate_handler.go
Normal file
317
ca-server/internal/handlers/certificate_handler.go
Normal file
@@ -0,0 +1,317 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
116
ca-server/internal/handlers/login_handler.go
Normal file
116
ca-server/internal/handlers/login_handler.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"ca-mini/pkg/utils"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type TokenInfo struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
Name string `json:"name"`
|
||||
Introduction string `json:"introduction"`
|
||||
Avatar string `json:"avatar"`
|
||||
Roles []string `json:"roles"`
|
||||
}
|
||||
|
||||
type LoginResponse struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Date string `json:"date"`
|
||||
Data TokenInfo `json:"data"`
|
||||
}
|
||||
type LogutResponse struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type UserInfoResponse struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Date string `json:"date"`
|
||||
Data UserInfo `json:"data"`
|
||||
}
|
||||
|
||||
// IssueCertificate 处理证书签发请求
|
||||
func Login(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// 解析CSR请求
|
||||
var loginRequest LoginRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&loginRequest); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 检查用户
|
||||
if !CheckUser(loginRequest) {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// 生成Token
|
||||
token, err := utils.GenerateRandomString(32)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to generate token: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// 返回Token信息
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
response := LoginResponse{
|
||||
Code: http.StatusOK,
|
||||
Message: "Login successful",
|
||||
Date: time.Now().Format(time.RFC3339),
|
||||
Data: TokenInfo{Token: token},
|
||||
}
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
func Logout(w http.ResponseWriter, r *http.Request) {
|
||||
// 这里可以添加注销逻辑,例如清除用户会话等
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
response := LogutResponse{
|
||||
Code: http.StatusOK,
|
||||
Message: "Logout successful",
|
||||
}
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
// CheckBlacklist 处理黑名单查询请求
|
||||
func CheckUser(login LoginRequest) bool {
|
||||
if login.Username == "admin" && login.Password == "111111" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetUserInfo(w http.ResponseWriter, r *http.Request) {
|
||||
// 假设用户信息存储在某个地方,这里直接返回一个示例用户信息
|
||||
userInfo := UserInfo{
|
||||
Name: "Admin User",
|
||||
Introduction: "This is an admin",
|
||||
Avatar: "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif",
|
||||
Roles: []string{"admin"},
|
||||
}
|
||||
// 返回Token信息
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
response := UserInfoResponse{
|
||||
Code: http.StatusOK,
|
||||
Message: "Login successful",
|
||||
Date: time.Now().Format(time.RFC3339),
|
||||
Data: userInfo,
|
||||
}
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
Reference in New Issue
Block a user