package api import ( "encoding/base64" "net/http" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/stef/pkiapi/internal/pki" "github.com/stef/pkiapi/internal/storage" ) // CreateCARequest représente la requête de création d'une CA type CreateCARequest struct { Subject string `json:"subject" binding:"required"` ValidityDays int `json:"validity_days" binding:"required,min=1,max=36500"` } // CAResponse représente la réponse avec les données de la CA type CAResponse struct { ID string `json:"id"` Subject string `json:"subject"` NotBefore string `json:"not_before"` NotAfter string `json:"not_after"` SerialNumber string `json:"serial_number"` Certificate string `json:"certificate"` // Base64 encoded PrivateKey string `json:"private_key,omitempty"` // Base64 encoded (optional) IsCA bool `json:"is_ca"` } // caStore est un store global pour les CAs var caStore storage.CertificateStore // InitCAStore initialise le store pour les CAs func InitCAStore(store storage.CertificateStore) { caStore = store } // ListCAResponse représente une CA dans une liste type ListCAResponse struct { ID string `json:"id"` Subject string `json:"subject"` Issuer string `json:"issuer"` NotBefore string `json:"not_before"` NotAfter string `json:"not_after"` SerialNumber string `json:"serial_number"` } // ListCAs retourne toutes les CAs func ListCAs(c *gin.Context) { cas := caStore.ListCertificates() var responses []ListCAResponse for _, ca := range cas { if ca.IsCA { response := ListCAResponse{ ID: ca.ID, Subject: ca.Subject, Issuer: ca.Issuer, NotBefore: ca.NotBefore.Format("2006-01-02T15:04:05Z"), NotAfter: ca.NotAfter.Format("2006-01-02T15:04:05Z"), } if ca.Cert != nil { response.SerialNumber = ca.Cert.SerialNumber.String() } responses = append(responses, response) } } c.JSON(http.StatusOK, gin.H{"cas": responses}) } // CreateCA crée une nouvelle autorité de certification func CreateCA(c *gin.Context) { var req CreateCARequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Générer la CA ca, err := pki.GenerateCA(req.Subject, req.ValidityDays) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur génération CA"}) return } // Générer un ID unique caID := uuid.New().String() ca.ID = caID // Sauvegarder la CA if err := caStore.SaveCertificate(caID, ca); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur sauvegarde CA"}) return } // Récupérer l'utilisateur depuis le contexte JWT userID, _ := c.Get("user_id") // Retourner la réponse response := CAResponse{ ID: caID, Subject: ca.Subject, NotBefore: ca.NotBefore.Format("2006-01-02T15:04:05Z"), NotAfter: ca.NotAfter.Format("2006-01-02T15:04:05Z"), IsCA: true, } if ca.Cert != nil { response.SerialNumber = ca.Cert.SerialNumber.String() response.Certificate = base64.StdEncoding.EncodeToString(ca.Cert.Raw) } // Ajouter la clé privée if ca.PrivateKey != nil { privKeyBase64, err := encodePrivateKey(ca.PrivateKey) if err == nil { response.PrivateKey = privKeyBase64 } } c.JSON(http.StatusCreated, gin.H{ "ca": response, "created_by": userID, }) } // GetCA retourne une CA par ID func GetCA(c *gin.Context) { id := c.Param("id") ca, err := caStore.GetCertificate(id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "CA non trouvée"}) return } if !ca.IsCA { c.JSON(http.StatusBadRequest, gin.H{"error": "ce certificat n'est pas une CA"}) return } response := CAResponse{ ID: ca.ID, Subject: ca.Subject, NotBefore: ca.NotBefore.Format("2006-01-02T15:04:05Z"), NotAfter: ca.NotAfter.Format("2006-01-02T15:04:05Z"), IsCA: true, } if ca.Cert != nil { response.SerialNumber = ca.Cert.SerialNumber.String() response.Certificate = base64.StdEncoding.EncodeToString(ca.Cert.Raw) } // Ajouter la clé privée if ca.PrivateKey != nil { privKeyBase64, err := encodePrivateKey(ca.PrivateKey) if err == nil { response.PrivateKey = privKeyBase64 } } c.JSON(http.StatusOK, gin.H{"ca": response}) } // SignCertificateRequest représente une requête de signature de certificat par une CA type SignCertificateRequest struct { CAId string `json:"ca_id" binding:"required"` Subject string `json:"subject" binding:"required"` ValidityDays int `json:"validity_days" binding:"required,min=1,max=3650"` } // SignCertificateWithCA signe un certificat avec une CA func SignCertificateWithCA(c *gin.Context) { var req SignCertificateRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Récupérer la CA ca, err := caStore.GetCertificate(req.CAId) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "CA non trouvée"}) return } if !ca.IsCA { c.JSON(http.StatusBadRequest, gin.H{"error": "ce certificat n'est pas une CA"}) return } // Signer le certificat avec la CA cert, err := pki.SignCertificate(ca, req.Subject, req.ValidityDays) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur signature certificat: " + err.Error()}) return } // Générer un ID unique certID := uuid.New().String() cert.ID = certID // Sauvegarder le certificat if err := certificateStore.SaveCertificate(certID, cert); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur sauvegarde certificat"}) return } // Récupérer l'utilisateur depuis le contexte JWT userID, _ := c.Get("user_id") // Retourner la réponse response := CertificateResponse{ ID: certID, Subject: cert.Subject, Issuer: cert.Issuer, NotBefore: cert.NotBefore.Format("2006-01-02T15:04:05Z"), NotAfter: cert.NotAfter.Format("2006-01-02T15:04:05Z"), Revoked: false, } if cert.Cert != nil { response.SerialNumber = cert.Cert.SerialNumber.String() response.Certificate = base64.StdEncoding.EncodeToString(cert.Cert.Raw) } // Ajouter la clé privée if cert.PrivateKey != nil { privKeyBase64, err := encodePrivateKey(cert.PrivateKey) if err == nil { response.PrivateKey = privKeyBase64 } } c.JSON(http.StatusCreated, gin.H{ "certificate": response, "signed_by": req.CAId, "created_by": userID, }) } // SignSubCARequest représente une requête de création de sub-CA type SignSubCARequest struct { ParentCAId string `json:"parent_ca_id" binding:"required"` Subject string `json:"subject" binding:"required"` ValidityDays int `json:"validity_days" binding:"required,min=1,max=36500"` } // SignSubCA crée une CA intermédiaire signée par une CA parent func SignSubCA(c *gin.Context) { var req SignSubCARequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Récupérer la CA parent parentCA, err := caStore.GetCertificate(req.ParentCAId) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "CA parent non trouvée"}) return } if !parentCA.IsCA { c.JSON(http.StatusBadRequest, gin.H{"error": "ce certificat n'est pas une CA"}) return } // Signer la sub-CA avec la CA parent subCA, err := pki.SignSubCA(parentCA, req.Subject, req.ValidityDays) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur signature sub-CA: " + err.Error()}) return } // Générer un ID unique subCAId := uuid.New().String() subCA.ID = subCAId // Sauvegarder la sub-CA if err := caStore.SaveCertificate(subCAId, subCA); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur sauvegarde sub-CA"}) return } // Récupérer l'utilisateur depuis le contexte JWT userID, _ := c.Get("user_id") // Retourner la réponse response := CAResponse{ ID: subCAId, Subject: subCA.Subject, NotBefore: subCA.NotBefore.Format("2006-01-02T15:04:05Z"), NotAfter: subCA.NotAfter.Format("2006-01-02T15:04:05Z"), IsCA: true, } if subCA.Cert != nil { response.SerialNumber = subCA.Cert.SerialNumber.String() response.Certificate = base64.StdEncoding.EncodeToString(subCA.Cert.Raw) } // Ajouter la clé privée if subCA.PrivateKey != nil { privKeyBase64, err := encodePrivateKey(subCA.PrivateKey) if err == nil { response.PrivateKey = privKeyBase64 } } c.JSON(http.StatusCreated, gin.H{ "ca": response, "signed_by": req.ParentCAId, "created_by": userID, }) }